【问题标题】:Querying large SQLite DB causes UI freezing even if performed in another thread查询大型 SQLite DB 会导致 UI 冻结,即使在另一个线程中执行也是如此
【发布时间】:2016-07-05 21:15:50
【问题描述】:

我的应用预装了三个数据库。一个很大,有 293,092 行,其中大部分是长句子。我的问题是每当我在工作线程中查询(rawQuery)大的时,UI 可能会冻结 1 秒。

这是我的查询语句:

SELECT verse
FROM Translations
WHERE translatorId = ?
AND chapterNbr = ?
AND verseNbr BETWEEN ? AND ?
ORDER BY chapterNbr , verseNbr

这里的问题是即使我故意只查询 1 行(1 个结果),UI 仍然冻结。

请帮忙。非常感谢。

这是我很长的代码:

static void setVerseAdapter(final int[][] refs) {

    surahThread = new Thread(new Runnable() {
        @Override
        public void run() {         
                Cursor cursor = Translations.getTranslators();
                final int[] translationIds = new int[cursor.getCount()];
                while (cursor.moveToNext()) {
                    translationIds[cursor.getPosition()] = cursor.getInt(0);
                }

               adapterVerses = new AdapterVerses
                        (context, getVersesArray(refs), Translations.getTranslationsArray(translationIds, refs)); // this one is the problem, the getTranslationsArray              

            // remaining code in this method is irrelevant ...
    });

    surahThread.start();
}

static Cursor[][] getTranslationsArray(int[] translationIds, int[]... refs) {

    if (!Settings.showTranslation)
        return new Cursor[][]{{}};

    Cursor[][] translations = new Cursor[refs.length][];

    for (int i = 0; i < refs.length; i++) {
        int start = refs[i][0];
        int end = refs[i][1];
        int surahNbr = refs[i][2];
        translations[i] = getTranslations(translationIds, start, end, surahNbr);
    }

    return translations;
}


static Cursor[] getTranslations(int[] translatorIds, int start, int end, int surahNbr) {

    Cursor[] translations = new Cursor[translatorIds.length];

    for (int i = 0; i < translatorIds.length; i++) {
        translations[i] = readTrans(
                "SELECT verse " +
                        "FROM Translations " +
                        "WHERE translatorId = ? " +
                        "AND surahNbr = ? " +
                        "AND verseNbr BETWEEN ? AND ? " +
                        "ORDER BY surahNbr, verseNbr",
                translatorIds[i], surahNbr + 1, start, end
        );
    }

    return translations;
}

static Cursor readTrans(String query, Object... args) {
    SQLiteDatabase transDb = transExists ? DB.trans.db : DB.user.db; // using user.db results to no freezing
    return transDb.rawQuery(replaceArgs(query, args), null);
}

当我使用 DB.user.db 而不是 DB.trans.db 时,上面的一切都可以正常工作。 它们之间的区别在于它们的大小。第一个只有 ~4MB,第二个(有问题的)在解压后约为 65MB

谢谢。

【问题讨论】:

  • 请提供minimal reproducible example。这将包括您查询数据库的代码和您使用从查询返回的Cursor 的代码。
  • 我已经更新了我的问题。谢谢。
  • 你的代码……令人费解。您还在快速连续地进行大量查询,这通常会导致性能下降。特别是关于线程,请确保您对每个 Cursor 执行操作,当您在后台线程上时 从数据库返回。 query()rawQuery() 等方法实际上并不执行查询。他们设置了Cursors,但直到您实际使用Cursor (惰性评估)执行某些操作时才完成查询。如果不出意外,请致电getCount()
  • 你测量时间了吗?我敢打赌,问题出在适配器本身...我敢打赌,您使用了一种愚蠢的解决方案来在可滚动的内部滚动,从而避免在列表视图中重复使用正确的视图
  • 我的最后一次编辑... [当我使用 DB.user.db 而不是 DB.trans.db 时,上面的一切都可以正常工作。它们之间的区别在于它们的大小。第一个只有约 4MB,第二个(有问题的)在解压后约为 65MB。] ... 除了完全重构我的代码之外,您还能想出另一种解决方案吗?也许关于上面的陈述?谢谢。

标签: java android database multithreading sqlite


【解决方案1】:

您可以使用 IntentService 在远离 Activity 的地方运行查询,然后通过 intent.putExtra 将查询结果发送回 Activity..

【讨论】:

    【解决方案2】:

    我通过简单地索引有问题的表来解决它(是的,我没有索引它,因为我是一个完全的菜鸟)。非索引查询耗时约 600 毫秒,索引查询仅需 5 毫秒。

    (我仍然无法理解的奇怪部分是为什么它会冻结我的 UI?不应该在我刚刚创建的后台线程中完成查询吗?)

    【讨论】:

      【解决方案3】:

      问题是你在等待数据被查询。是否将数据库操作移至另一个线程无关紧要;当 UI 线程等待其他线程完成时,它仍然阻塞。

      您应该只查询您实际需要显示的数据,并确保数据库操作得到适当的优化。 但如果查询仍然太慢,避免冻结 UI 的唯一方法是在没有数据的情况下显示 UI。

      【讨论】:

        猜你喜欢
        • 2023-04-06
        • 2015-10-23
        • 1970-01-01
        • 1970-01-01
        • 2018-10-06
        • 2018-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多