2009-09-10 16 views
18

Wir erstellen eine App für einen Client, der Hunderte von Megabyte HTML in SQLite-Datenbanken hat. Wir haben eine Methode implementiert, um diese Daten abzufragen und in einer relativ schnellen Art und Weise durch sie zu scrollen. Das Problem ist, dass einige der Datenbanken sehr große Abfragen haben (mehr als 20.000 Zeilen) und wir sehen Fehler, wenn wir die Abfragen beim Scrollen des Benutzers vergrößern. Ich denke also, die Frage ist, welche Möglichkeiten wir haben, Zehntausende von Datenzeilen in Android abzufragen und anzuzeigen? HierAndroid SQLite und große Datenmengen

ist die stacktrace wir sehen:

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 

Hier ist unser Adapter Cursor-Code:

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

"Welche Optionen haben wir bei der Abfrage und Anzeige von Zehntausenden von Datenzeilen in Android?" -> Wie? Ich kann nicht einmal denken, warum du das vielleicht willst. – SK9

+0

@ SK9 Dies war für die Portierung einer medizinischen Referenz App vom iPhone auf Android. Sie hatten buchstäblich Zehntausende von Datensätzen in diesen SQLite-Datenbanken. Jede vernünftige Person würde beginnen, einen Suchbegriff einzugeben, um die Liste einzugrenzen, aber die iPhone App unterstützt das manuelle Scrollen durch alle Daten auch völlig in Ordnung. Sie erwarteten daher, dass sich die Android-App genauso verhält. – MattC

+0

Ich habe eine ähnliche Anforderung. Man wäre erstaunt, wie viele Organisationen auf diese Weise spezialisierte Apps nutzen. – gonzobrains

Antwort

26

Welche Möglichkeiten haben wir in Abfrage und Zehntausende von Anzeige Datenzeilen in Android?

Sie meinen, außer Ihnen zu sagen, dass 20.000+ Reihen auf einem 3,5" LCD ist Fledermaus-Guano verrückt zu lesen? ;-)

Es ist wie CursorWindow sieht, die sich unter den Abdeckungen irgendwo verwendet wird, ist mit Problemen Verwaltung> 17.000 Zeilen, die eines von zwei Dingen sein könnten.

  1. Sie sind aus Heap-Speicher mit 16 MB nicht-Verdichtungs Haufen, und der Tatsache, dass ein Cursor das gesamte Ergebnis in der Halde gesetzt hält, dass. kommt nicht in Frage
  2. CursorWindow unterstützt nur 1 MB Daten, was die Fehlermeldung direkt vorschlägt.

Wenn es eine logische Art und Weise ist Ihre Anfragen in einzelne Stücke zu teilen, können Sie inkrementelle Abfragen tun und verwenden CursorJoiner sie zusammen zu nähen, und sehen, ob das hilft.

Aber alles Ernst, 20.000+ Reihen in einem 3,5" -Bildschirm, auf einem Gerät, das am ehesten einen 12-jährigen PC in PS ähnelt sind wirklich viel zu fragen.

+0

Leider funktioniert es wie ein Zauber auf dem iPhone und der Client erwartet vergleichbare Leistung. In diesem Fall bricht es einfach nur aus und das ist alles schlecht. Wir brechen die Abfrage auf, um jeweils einige Zeilen zu laden. Das Problem ist, wenn sie auf den Grund "schleudern" und erwarten, dass sie am Ende der Liste stehen. – MattC

+3

* shrug * Finden Sie einen Weg, um Ihre Abfrage Ergebnisse unter 1MB zu halten. Fordern Sie weniger Spalten an. Packen Sie Ihre Daten besser (verwenden Sie zum Beispiel nicht 8 INTEGER-Spalten für einen booleschen Wert - verwenden Sie eine INTEGER-Spalte und Bitmasken). Stellen Sie sich ein UX vor, das dem Benutzer ermöglicht, konsistent besser zu filtern, so dass Sie nicht in einen 20k-Zeilen-Ergebnissatz gelangen. Sie können versuchen, SQLiteCursor/SQLiteQuery mit einem CursorWindow zu umgehen, aber ich vermute, dass Ihre Leistung unangenehm sein wird. – CommonsWare

+0

Ich frage mich dann, was ist der beste Weg, um durch eine Liste reibungslos zu scrollen, wenn Sie einen Tisch mit 42.000 Artikel haben und Sie laden 30-40 auf einmal. Es funktioniert für kleinere Tabellen, aber die Verwendung von LIMIT und OFFSET in der Abfrage für diese spezielle Datenbank verursacht immer noch ANRs und Schluckauf beim Scrollen. – MattC

5

Wenn Sie wirklich brauchen dass Sie können auch Ihre Daten und lesen Stücke wie folgt aufgeteilt:.

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

Es ist unter 2 s für 5k Reihen arbeiten, wenn Sie indizierte Tabelle haben

Danke, Arkde

0

Nach meiner Erfahrung dauert es aufgrund der Einschränkung von Abfragen viel länger, Ergebnisse zu erhalten, da das Starten neuer Cursor bei niedrigen Limits teuer ist. Ich habe gerade 62k Zeilen mit 1k und sogar 10k Limit versucht und es ist sehr langsam und unbrauchbar, da ich mehr als 6 Cursor starten muss. Ich bleibe nur bei der Unterstützung von 2.3.3 .... Es ist der neueste Build, dass ich CursorWindow ERROR anstelle von WARN bekomme.

Hier ist was ich getan habe. Dies ist wahrscheinlich der beste Algorithmus, den ich denke. Sie müssen keine Abfragen zweimal durchführen usw. Allerdings kann es bei großen Abfragen und kleinen Grenzwerten ziemlich langsam werden, so dass Sie testen müssen, was am besten funktioniert. In meinem Fall ist es nicht schnell genug für meine Zwecke, da es 62k Zeilen nicht gut verarbeitet.

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 
} 
Verwandte Themen