2009-09-10 6 views
18

Nous créons une application pour un client qui a des centaines de mégaoctets de HTML dans les bases de données SQLite. Nous avons mis en place un moyen d'interroger ces données et de les faire défiler assez rapidement. Le problème est que certaines bases de données ont des requêtes très volumineuses (plus de 20 000 lignes) et que nous observons des erreurs lorsque nous développons les requêtes au fur et à mesure que l'utilisateur défile. Donc, je suppose que la question est, quelles options avons-nous dans l'interrogation et l'affichage de dizaines de milliers de lignes de données dans Android?Android SQLite et d'énormes ensembles de données

Voici le stacktrace que nous voyons:

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 

Voici notre code 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

« quelles options nous avons dans l'interrogation et l'affichage des dizaines de milliers de lignes de données dans Android? » -> hein? Je ne peux même pas imaginer pourquoi vous pourriez vouloir faire cela. – SK9

+0

@ SK9 Ce fut pour le portage d'une application de référence médicale de l'iPhone à Android. Ils avaient littéralement des dizaines de milliers d'enregistrements dans ces bases de données SQLite. Toute personne sensée commencerait à entrer dans un terme de recherche pour affiner la liste, mais l'application iPhone supportant manuellement le défilement de toutes les données parfaitement bien aussi. Ainsi, ils s'attendaient à ce que l'application Android se comporte de la même manière. – MattC

+0

J'ai une exigence similaire. On serait étonné de voir combien d'organisations utilisent des applications spécialisées de cette manière. – gonzobrains

Répondre

26

quelles options nous avons dans l'interrogation et d'afficher des dizaines de milliers de des rangées de données dans Android?

Vous voulez dire en plus de vous dire que la lecture de lignes sur une 20.000+ 3,5" LCD est bat-guano fou? ;-)

Il ressemble à CursorWindow, qui est utilisé quelque part sous les couvertures, a des problèmes gestion> 17.000 lignes pourrait être l'une des deux choses.

  1. Vous êtes hors de l'espace de tas avec un tas non compactage 16MB, et le fait que Cursor détient tout le résultat mis dans le tas, que. n'est pas hors de question:
  2. CursorWindow ne prend en charge que 1 Mo de données, ce que le message d'erreur suggère plus directement.

S'il existe un moyen logique de diviser vos requêtes en morceaux discrets, vous pouvez effectuer des requêtes supplémentaires et d'utiliser CursorJoiner pour les assembler, et voir si cela aide.

Mais, sérieusement, dans les lignes 20.000+ écran 3.5" , sur un dispositif qui se rapproche le plus d'un PC en puissance, âgé de 12 ans, demande vraiment beaucoup.

+0

Malheureusement, cela fonctionne comme un charme sur l'iPhone et le client s'attend à des performances comparables. Dans ce cas, c'est juste à plat et c'est tout mauvais. Nous cassons la requête pour charger quelques lignes à la fois. Le problème est quand ils "jettent" vers le bas et s'attendent à aller au bas de la liste. – MattC

+3

* hausser les épaules * Trouver un moyen de garder les résultats de votre requête sous 1 Mo. Demander moins de colonnes Emballez mieux vos données (par exemple, n'utilisez pas 8 colonnes INTEGER chacune pour une valeur booléenne - utilisez une colonne INTEGER et des masques de bits). Venez avec un UX qui permet à l'utilisateur de filtrer toujours mieux, de sorte que vous ne rencontrerez pas un ensemble de résultats de 20K lignes. Vous pouvez essayer de contourner SQLiteCursor/SQLiteQuery en utilisant un CursorWindow, mais je soupçonne que vos performances seront mauvaises. – CommonsWare

+0

Je suppose que je me demande alors quelle est la meilleure façon de faire défiler une liste en douceur lorsque vous avez une table avec 42 000 articles et que vous chargez 30-40 à la fois. Cela fonctionne pour les tables plus petites, mais l'utilisation de LIMIT et OFFSET dans la requête pour cette base de données particulière provoque toujours ANR et hiccups dans le défilement. – MattC

5

Si vous avez vraiment besoin que vous pouvez également partager vos données et lire des morceaux comme ceci:.

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

Il travaille moins de 2 s pour 5k lignes si vous avez table indexée

Merci, Arkde

0

D'après mon expérience, la limitation des requêtes nécessite beaucoup plus de temps pour obtenir des résultats car le démarrage de nouveaux curseurs est coûteux pour les limites basses. J'ai essayé de faire 62k lignes avec une limite de 1k et même 10k tout à l'heure et c'est très lent et inutilisable puisque je dois commencer plus de 6 curseurs. Je vais m'en tenir à ne pas supporter 2.3.3 .... C'est la dernière version que j'obtiens CursorWindow ERROR au lieu de WARN.

Voici ce que j'ai fait. C'est probablement le meilleur algorithme que je pense. Ne pas avoir à faire deux fois des requêtes, etc. Cependant, cela peut devenir assez lent avec de grosses requêtes et de petites limites, donc vous devez tester ce qui fonctionne le mieux. Dans mon cas, ce n'est pas assez rapide pour mes besoins car il ne gère pas bien 62k lignes.

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 
} 
Questions connexes