Android SQLite y enormes conjuntos de datos

Estamos creando una aplicación para un cliente que tiene cientos de megabytes de HTML en bases de datos SQLite. Hemos implementado una forma de consultar estos datos y desplazarnos por todo de una manera razonablemente rápida. El problema es que algunas de las bases de datos tienen consultas muy grandes (más de 20,000 filas) y estamos viendo errores cuando aumentamos las consultas a medida que el usuario se desplaza. Así que supongo que la pregunta es, ¿qué opciones tenemos para consultar y mostrar decenas de miles de filas de datos en Android?

Aquí está la stacktrace que estamos viendo:

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 

Aquí está nuestro código 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()); } } 

¿Qué opciones tenemos para consultar y mostrar decenas de miles de filas de datos en Android?

¿Quiere decir además de decirle que leer más de 20,000 filas en una pantalla LCD de 3.5 “es una locura de guano de murciélago? 😉

Parece que CursorWindow , que se usa en algún lugar bajo las cubiertas, tiene problemas para administrar> 17,000 filas. Esa podría ser una de dos cosas:

  1. Estás fuera del montón de espacio. Con un montón de 16MB sin compactación, y el hecho de que un Cursor contenga todo el conjunto de resultados en el montón, eso no está fuera de discusión.
  2. CursorWindow solo admite 1MB de datos, que es lo que el mensaje de error sugiere más directamente.

Si hay una forma lógica de dividir las consultas en fragmentos discretos, puede hacer consultas incrementales y usar CursorJoiner para unirlas y ver si eso ayuda.

Pero, con toda seriedad, más de 20,000 filas en una pantalla de 3.5 “, en un dispositivo que se asemeja más a una PC de 12 años en potencia, realmente está pidiendo mucho.

Si realmente lo necesita, también puede dividir sus datos y leer fragmentos como este:

  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(); 

Está trabajando por debajo de 2 s para 5k filas si tiene una tabla indexada.

Gracias, Arkde

En mi experiencia, la limitación de consultas hace que tome mucho más tiempo obtener resultados porque iniciar nuevos cursores es costoso para los límites bajos. Intenté hacer 62k filas con 1k y hasta 10k de límite justo ahora y es muy lento e inutilizable ya que tengo que iniciar más de 6 cursores. Me limitaré a no soportar 2.3.3 …. Es la última versión que obtengo CursorWindow ERROR en lugar de WARN.

Esto es lo que hice sin embargo. Este es probablemente el mejor algoritmo, creo. No tiene que hacer consultas dos veces, etc. Sin embargo, puede ser bastante lento con grandes consultas y pequeños límites, por lo que debe probar lo que funciona mejor. En mi caso, no es lo suficientemente rápido para mis propósitos, ya que no maneja bien 62k filas.

 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 }