Android SQLite快速从光标获取所有数据

时间:2013-04-20 22:39:46

标签: android database performance sqlite cursor

我正在处理其中一个应用中的性能问题。也许你们其中一个人可以帮助我?!

我有一个数据库,大约有10k个条目。 我使用SQLiteDatabase - 类中的默认查询方法查询元素。 查询本身足够快。

查询完成后,我必须在Google地图上显示结果。 为此,我从光标生成一个结果数组,用于保存标记信息。

我使用的方法看起来像这样:

    final ArrayList< MarkerElement > result = new ArrayList< MarkerElement >();
    cursor.moveToFirst();
    while ( !cursor.isAfterLast() ) {
        result.add( new MarkerElement(
                cursor.getString( COL_TITLE ),
                cursor.getString( COL_SNIPPET ),
                new LatLng(
                        cursor.getDouble( COL_LAT ),
                        cursor.getDouble( COL_LNG ) ),
                cursor.getString( COL_OTHER_USEFUL_DATA ) );
        cursor.moveToNext();
    }
    cursor.close();

其中 MarkerElement 只是一个在Google地图上保存标记所需值的类。

现在的问题是,循环遍历所有游标元素需要很长时间。 另外,我想不出像ListView一样懒惰加载结果的聪明方法,因为我需要同时显示所有结果。

我能做些什么来显着加快这个过程吗?

非常感谢任何帮助!

祝你好运

1 个答案:

答案 0 :(得分:5)

我不太确定查询是否真的像你想象的那么快。很可能实际查询仅使用cursor.moveToFirst()语句执行,而不是在调用query()或rawQuery()(或您正在使用的任何其他查询方法)时执行。

无论如何,查询应该足够快以使用户很快就能等待。如果没有,那么您可能需要考虑使用SELECT * FROM your_table LIMIT START,COUNT(例如SELECT * FROM your_table LIMIT 0,1000来检索前1000行)将其加载到块中。

查询不能在ui线程上发生,因此您希望在AsyncTaskLoader或更好的CursorLoader中运行它。您可以在此处看到没有ContentProvider的CursorLoader:https://stackoverflow.com/a/7422343/534471

假设您需要为每个1000条记录运行10个查询,然后您将拥有10个可以使用LoaderManager管理的CursorLoaders。 LoaderManager管理游标(打开和关闭它们),在方向更改中保留游标并在后台任务中运行所有内容,因此阻塞ui线程没有问题。如果内容发生变化,LoaderManager也会重新查询数据库(参见:https://stackoverflow.com/a/5603959/534471)。 当LoaderManager通知您的片段或您的活动光标已完成加载时,它将调用onLoadFinished()(参见:http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html)。

减慢代码的速度不仅仅是查询数据库,还要创建10'000 MarkerElement和另外10'000 LatLng对象。我不知道你的要求,但如果你看到没有这些对象的任何机会,肯定会加速你的代码。消除MarkerElements / LatLng的另一个理想效果是内存使用。对于针对手机的应用,20个具有3个字符串和2个双倍的对象是相当可观的。

使用CursorLoaders和LoaderManager将允许您从光标检索值并直接填充您的ui视图而无需MarkerElements和LatLng。它还允许你懒洋洋地加载。您可以在为其中一个CursorLoaders调用onLoadFinished()时填充视图(除非从非ui线程调用initLoader / restartLoader,否则在ui线程上调用onLoadFinished())。如果一次填充1000个视图太多,要么将查询分解为更小的部分,要么添加一个机制来以10s或100s的块(对于每个游标)填充视图。

如果您需要当前存储在MarkerElement中的信息,例如当用户选择其中一个标记时,在视图上使用setTag(),该视图显示用于存储db记录主键的标记。使用密钥从数据库中检索记录甚至更好地从已查询的游标中检索记录(这将需要一些映射机制,但它是可行的)。

要点:

  • 将查询拆分为多个子查询以检索较小的数据集
  • 使用CursorLoaders和CursorManager管理不同的查询/游标
  • 不要为每一行创建MarkerElement和LatLng,而是直接从返回的游标填充视图
  • 可能会在几个步骤中为每个光标填充以保持ui响应
  • 使用视图上的setTag()来检索视图后面的数据