2

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

我有一个大约有 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 中的结果,因为我需要同时显示所有结果。

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

任何帮助是极大的赞赏!

此致

4

1 回答 1

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 管理的 CursorLoader。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 的另一个理想效果是内存使用。对于一个以手机为目标的应用程序,包含 3 个字符串和 2 个 Double 的 20'000 个对象是相当可观的。

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

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

概括:

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