我们正在为SQLite数据库中具有数百兆字节HTML的客户端创建一个应用程序。我们已经实现了一种查询此数据的方法,并以合理快速的方式滚动浏览所有数据。问题是某些数据库有非常大的查询(20,000多行),当我们在用户滚动时增加查询时,我们会看到错误。所以我想问题是,我们在Android中查询和显示数万行数据时有哪些选择?
这是我们看到的堆栈跟踪:
09-10 19:19:12.575: WARN/IInputConnectionWrapper(640): showStatusIcon on inactive InputConnection
09-10 19:19:18.226: DEBUG/dalvikvm(640): GC freed 446 objects / 16784 bytes in 330ms
09-10 19:19:32.886: ERROR/CursorWindow(19416): need to grow: mSize = 1048576, size = 36, freeSpace() = 30, numRows = 17717
09-10 19:19:32.896: ERROR/CursorWindow(19416): not growing since there are already 17717 row(s), max size 1048576
09-10 19:19:32.916: ERROR/CursorWindow(19416): The row failed, so back out the new row accounting from allocRowSlot 17716
09-10 19:19:33.005: ERROR/Cursor(19416): Failed allocating fieldDir at startPos 0 row 17716
09-10 19:19:35.596: DEBUG/Cursor(19416): finish_program_and_get_row_count row 24315
09-10 19:19:41.545: DEBUG/dalvikvm(698): GC freed 2288 objects / 126080 bytes in 260ms
09-10 19:19:43.705: WARN/KeyCharacterMap(19416): No keyboard for id 0
09-10 19:19:43.717: WARN/KeyCharacterMap(19416): Using default keymap: /system/usr/keychars/qwerty.kcm.bin
09-10 19:20:04.705: ERROR/CursorWindow(19416): need to grow: mSize = 1048576, size = 17, freeSpace() = 3, numRows = 17094
09-10 19:20:04.716: ERROR/CursorWindow(19416): not growing since there are already 17094 row(s), max size 1048576
09-10 19:20:04.726: ERROR/Cursor(19416): Failed allocating 17 bytes for text/blob at 17093,2
09-10 19:20:05.656: DEBUG/Cursor(19416): finish_program_and_get_row_count row 5257
09-10 19:24:54.685: DEBUG/dalvikvm(637): GC freed 9297 objects / 524176 bytes in 247ms
09-10 19:32:07.656: DEBUG/dalvikvm(19416): GC freed 9035 objects / 495840 bytes in 199ms
这是我们的CursorAdapter代码:
private class MyAdapter extends ResourceCursorAdapter {
public MyAdapter(Context context, Cursor cursor) {
super(context, R.layout.my_row, cursor);
}
public void bindView(View view, Context context, Cursor cursor) {
RowData data = new RowData();
data.setName(cursor.getInt(cursor.getColumnIndex("name")));
TextView tvItemText = (TextView)view.findViewById(R.id.tvItemText);
tvItemText.setText(data.getName());
view.setTag(data);
}
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
/* Display the progress indicator */
updateHandler.post(onFilterStart);
/* Run the actual query */
if (constraint == null) {
return myDbObject.getData(null);
}
return myDbObject.getData(constraint.toString());
}
}
答案 0 :(得分:26)
我们在查询时有哪些选择 并展示了成千上万的 Android中的数据行?
你的意思是除了告诉你在3.5英寸液晶显示器上阅读20,000多行是蝙蝠 - 鸟粪疯狂吗?; - )
看起来像CursorWindow
,它被用于幕后的某个地方,管理着大约17,000行的问题。这可能是两件事之一:
Cursor
将整个结果集保存在堆中的事实,这不是不可能的。CursorWindow
仅支持1MB的数据,这是错误消息更直接的建议。如果有一种逻辑方法可以将查询划分为离散块,则可以执行增量查询并使用CursorJoiner
将它们拼接在一起,看看是否有帮助。
但是,严肃地说,3.5英寸屏幕上的20,000多行,在最接近马力的12年PC上的设备上,确实需要很多。
答案 1 :(得分:6)
如果你真的需要,你也可以分割你的数据并阅读这样的块:
int limit = 0;
while (limit + 100 < numberOfRows) {
//Compose the statement
String statement = "SELECT * FROM Table ORDER someField LIMIT '"+ limit+"', 100";
//Execute the query
Cursor cursor = myDataBase.rawQuery(statement, null);
while (cursor.moveToNext()) {
Product product = new Product();
product.setAllValuesFromCursor(cursor);
productsArrayList.add(product);
}
cursor.close();
limit += 100;
}
//Compose the statement
String statement = "SELECT * FROM Table ORDER someField LIMIT '"+ (numberOfRows - limit)+"', 100";
//Execute the query
Cursor cursor = myDataBase.rawQuery(statement, null);
while (cursor.moveToNext()) {
Product product = new Product();
product.setAllValuesFromCursor(cursor);
productsArrayList.add(product);
}
cursor.close();
如果你有索引表,它在2秒内工作5k行。
谢谢, Arkde
答案 2 :(得分:0)
根据我的经验,限制查询会使得获得结果需要更长的时间,因为启动新游标对于低限制来说是昂贵的。我现在尝试用1k甚至10k限制做62k行,因为我必须启动超过6个游标,所以它非常慢且无法使用。我只是坚持不支持2.3.3 ....这是我得到CursorWindow ERROR而不是WARN的最新版本。
这是我做的。这可能是我认为最好的算法。不必再进行两次查询等。但是,对于大查询和小限制,它可能会变慢,因此您必须测试哪些方法效果最好。就我而言,它不够快,因为它不能很好地处理62k行。
int cursorCount = 0;
int limit = 1000; //whatever you want
while (true)
{
Cursor cursor = builder.query(mDatabaseHelper.getReadableDatabase(), columns, selection, selectionArgs, null, null, null, Integer.toString(limit));
if (cursor == null) {
return null;
} else if (!cursor.moveToFirst()) { //if it is empty, return null
return null;
}
cursorCount = cursor.getCount();
if (cursorCount % limit != 0)
{
return cursor;
}
limit+=1000; //same amount as the one above
}