【问题标题】:Android: Handling very large data sets in ContentProvider to avoid memory limitsAndroid:在 ContentProvider 中处理非常大的数据集以避免内存限制
【发布时间】:2012-11-23 07:10:00
【问题描述】:

我正在使用ContentProvider 查询数据库并返回Cursor 用于CursorLoader

ItemsActivity:

public class ItemsActivity extends SherlockActivity implements LoaderCallbacks<Cursor> {

    @Override
    public void onCreate(Bundle savedInstance) {
        ....
        getSupportLoaderManager().initLoader(LOADER_ID, null, this);
        ...
    }

    @Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
        return new CursorLoader(getApplicationContext(), itemsListUri, ...); 
    }

    ...
}

ItemsContentProvider:

public Cursor query(Uri uri, String[] projection, String selection, ...) {
    SqliteQueryBuilder builder = new SqliteQueryBuilder();
    builder.setTables(ItemsTable.NAME);
    return builder.query(db, projection, selection, ...);
}

活动有一个ListView,我使用CursorAdapter(通过LoaderCallbacks更新)来表示游标内的数据。

这工作正常,直到我需要在大型数据集中查找项目(例如,超过 30,000 行)。观察日志,我发现查找超出了内存限制,并且从结果游标中删除了一些行。

我的问题:当使用这样的游标时,处理非常大的数据集的最佳方法是什么?

我当前的解决方案是将ContentProvider 中的SQLite 查询分解为具有偏移和限制的查询序列,然后使用MergeCursor 类组合这些查询:

private static final int LIMIT = 5000;

// Ignoring projection, selection, etc for simplicity here
public Cursor query(Uri uri, String projection, String selection, ...) {
  List<Cursor> cursors = newList();
  int offset = 0;
  Cursor c = db.rawQuery("select * from items limit " + LIMIT + " offset " + offset, null);
  while (c.getCount() > 0) {
    cursors.add(c);
    offset += c.getCount();
    c = db.rawQuery("select * from items limit " + LIMIT + " offset " + offset, null);
  }
  return createMergedCursor(cursors);
}

private Cursor createMergedCursors(List<Cursor> cursors) {
    if (cursors.size() == 1) {
        return cursors.get(0);
    }
    return new MergeCursor(toCursorsArray(cursors));
}

这将加载所有数据,但在第一次进行查找时会有很长的延迟。执行多个查询时,列表视图大约 5 秒为空。

请注意,当我尝试单次查找(而不是批量查找)时,加载几乎是瞬时的,尽管在达到内存限制时滚动列表时会有轻微的暂停。

所以:

使用单个查询:快速更新列表视图,但达到滚动暂停和内存限制。

使用批处理查询:列表视图更新缓慢,但滚动流畅且未达到内存限制。

我想知道是否有更好的解决方案可以快速更新列表视图,但在滚动列表时也会根据需要获取更多数据。

Android 4.2.1、Nexus 7

【问题讨论】:

  • 为什么列表视图中必须有 30000 个项目?
  • 我的应用显示音乐库的内容:1100 位艺术家、3200 张专辑、36000 首曲目。我有一个包含每个列表视图的选项卡,顶部有一个搜索字段。 UI 适用于艺术家/专辑视图,但曲目视图存在上述问题。

标签: android sqlite android-contentprovider android-cursorloader


【解决方案1】:

移动设备不是为处理这些数据量而设计的。

但是,如果您真的想给可怜的用户施加如此大的滚动列表,您可以将其设计为仅按需加载条目的虚拟列表;见Android Endless List

注意:使用OFFSET 子句效率低下;详情请见Scrolling Cursor

【讨论】:

  • 感谢 EndlessAdapter 的提示。它可能接近我正在寻找的东西。乍一看,它似乎需要一个明确的“加载更多”操作。我正在寻找一个适配器,它会在列表滚动时自动加载下一批结果。需要更多调查...
  • Tapatalk 应用程序(用于阅读各种讨论论坛)是我想要实现的一个示例:滚动对话列表时,它会根据需要自动加载更多对话。底部有一个微调器图标,表明正在发生这种情况。
【解决方案2】:

我同意 CL 的观点,即您不应该这样做。这在移动设备上不是一个好主意,在台式机上也不是。谁想滚动 30000 个元素?做什么的?很可能用户只在寻找一个结果,不是吗?所以提供一种简单的方法来过滤结果集。

直到结果集小到可以实际使用(这与列表滚动良好不同 - 它的结果数量可能要少得多),您只需显示当前查询的命中总数,也许一些元素作为样本提供给用户。用户必须过滤列表以获得实际可用的大小。

【讨论】:

  • 感谢您的评论。我在 UI 上有点挣扎:采用的方法是显示所有结果,然后允许用户过滤到他们想要的内容。显示所有结果还允许用户仔细阅读列表并发现其中的内容,而不是假设用户总是确切地知道他们想看到什么。
  • 好吧,当滚动 30.000 个列表条目时,他们不会发现。但是,是的,有时他们不确定自己在追求什么。这就是为什么我建议展示一些示例(限制 xyz 查询)。您应该立即声明:找到 30.000 条记录。仅显示前 x 个结果。所以他们知道他们自己如果不进行过滤,他们将找不到他们正在寻找的东西。
猜你喜欢
  • 2013-01-11
  • 2014-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
  • 2013-04-19
相关资源
最近更新 更多