rawQuery() 始终立即执行,无论实际查询需要多长时间

rawQuery() always executes immediately, no matter how long the actual query takes

几个小时以来,我一直在绞尽脑汁试图理解为什么一个非常简单的 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

How can I make sure that the rawQuery() method executes synchronously?

在返回前 getCursorOrThrow()Cursor 上调用 getCount()

游标是SQLiteCursor,lazy-executes第一次使用数据时的查询。这是另一个“非常酷的想法,但对我们现在的做事方式来说很糟糕”。在后台线程上调用 getCount() 会强制 SQLiteCursor 实际加载其数据。