无论实际查询花费多长时间,rawQuery总是立即执行

问题描述

我已经花了好几个小时试图了解为什么一个简单明了的Android代码破坏了我应用程序中的基本同步。

下面的方法总是立即执行,尽管事实上它有一个sqliteDatabase.rawQuery()调用SQL查询花费多长时间都没关系:

// this method is always executed instantly,despite the rawQuery() call
private Cursor runRawQuerytest() {

    Log.i(TAG,"runRawQuerytest() started");
    long timer = System.currentTimeMillis();

    // SLOW_sql_QUERY runs at least a couple seconds:
    Cursor c = getDb().rawQuery( SLOW_sql_QUERY,null );

    timer = System.currentTimeMillis() - timer;
    // timer below always reports a few milliseconds:
    Log.i(TAG,"runRawQuerytest() finished in " + timer + "ms.");

    return c;
}

这是Logcat:

2020-10-06 23:57:07.609 ... I/MainActivity: runRawQuerytest() started
2020-10-06 23:57:07.621 ... I/MainActivity: runRawQuerytest() finished in 12ms. <<<< "incorrect" timer 

好像以某种方式内联了runRawQueryTest()方法。它本身不是问题(无论如何,谁在乎日志?),因为数据库查询实际上发生在调用runRawQueryTest()的方法中:

    public void onClick(View view) {
        long timer = System.currentTimeMillis();
        Log.i(TAG,"onClick()" );
        Cursor c = runRawQuerytest();
        //  database query actually happens here:
        Log.i(TAG,"cursor.getCount() " + c.getCount());
        if ( !c.isClosed() ) c.close();
        timer = System.currentTimeMillis() - timer;
        Log.i(TAG,"onClick() - done in " + timer + "ms. ");
    }

Logcat:

2020-10-06 23:57:07.609 ... I/MainActivity: onClick()
2020-10-06 23:57:07.609 ... I/MainActivity: runRawQuerytest() started
2020-10-06 23:57:07.621 ... I/MainActivity: runRawQuerytest() finished in 12ms.
2020-10-06 23:57:14.223 ... I/MainActivity: cursor.getCount() 1           <<<< actual query
2020-10-06 23:57:14.223 ... I/MainActivity: onClick() - done in 6614ms.   <<<< correct timer 

真正的问题是,在我的应用程序中,这些数据库调用是使用RxJava异步进行的。 rawQuery()包装在Observable中,如下所示:

// RxJava wrapper
public Single<Cursor> makeObservable(final String query) {
    return Single
            .fromCallable( () -> getCursorOrThrow(query) )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread());
}

您可能已经猜到了,Observable几乎立即发出一个项目,因此实际的SQL查询在onNext()调用之后运行。结果,主线程上进行了太多工作,UI混乱了,尤其是进度条。

如何确保rawQuery()方法同步执行?

我唯一的猜测是所有这些都是由于非理想的R8优化而发生的。但是我的项目具有非常标准的“开箱即用”设置。我试图解决问题,使用ProGuard的“ -dontoptimize”键并尝试使用不同的Gradle版本。不幸的是,它没有帮助。我的想法不多了。我在概念上做错了吗?非常感谢您的帮助!

要点: https://gist.github.com/DmitryOganezov/c52fd8fcfa4f5be3e6ae5016a6ef7b4d

解决方法

如何确保rawQuery()方法同步执行?

在返回之前,必须getCursorOrThrow()getCount()呼叫Cursor

Cursor是SQLiteCursor,在第一次使用数据时它会懒执行查询。这是“对于当今我们的工作方式非常可怕的非常酷的想法”之一。在后台线程上调用getCount()会强制SQLiteCursor实际加载其数据。