2013-06-11 3 views
0

J'ai copié le fichier de base de données du périphérique et l'ai ouvert dans le client SQLite. Même requête dans le client SQLite renvoie les résultats comme prévu, mais Android parfois renvoie le curseur vide. Je ne peux pas expliquer pourquoi parfois le curseur renvoie 0 lignes. L'objet SQLiteDatabase est toujours ouvert par application car il y a beaucoup de demandes à la base de données pour une tâche.Android. Curseur vide

SQLiteDatabase database = this.getReadableDatabase(); 
try { 
    Cursor cursor = database.rawQuery(query, null); 
    try { 
     Log.d("Cursor rows count size: ", String.valueOf(cursor.getCount())); 
     if(cursor.moveToFirst()) { 
      do {     
       Long a= cursor.getLong(cursor.getColumnIndex(TableA.a)); 
       String b = String.valueOf(cursor.getInt(cursor.getColumnIndex(TableA.b))); 
       String c = cursor.getString(cursor.getColumnIndex(TableA.c)); 
       String d = cursor.getString(cursor.getColumnIndex(TableA.d)); 
       String e = cursor.getString(cursor.getColumnIndex(TableA.e)); 

       Object record = new Object (a, b, c, d, e);    
       list.add(record);    
      } while (cursor.moveToNext()); 
     } 
    } finally { 
     cursor.close(); 
    }   
} finally { 

} 

Mise à jour

requête SQL (modifiée colonne et les noms de tables Il n'y a pas d'influence en raison de changement de nom.):

SELECT 
    a , 
    b , 
    c , 
    d , 
    e 
FROM TableA 
    INNER JOIN TableB ON(TableB._id=TableA.y_id) 
    LEFT JOIN TableC ON(TableC._id=TableA.x_id) 
WHERE active=1 AND xxx>1370090365365 AND xxx<1370867965365 
ORDER BY TableA.xxx DESC 

Comme je l'ai dit: cette requête fonctionne comme je m'y attendais et renvoie le résultat dans le client SQLite de bureau.

Mise à jour

En plus je reçois cet avertissement aimable malgré tous les curseurs et les objets SQLiteDatabase est enveloppé par ... enfin essayer et fermé.

W/CursorWrapperInner(10374): Cursor finalized without prior close() 
+0

partie la plus intéressante est 'query' variables, en fait. Pouvez-vous fournir sa valeur? –

+0

Aussi, dans l'extérieur enfin, fermez la base de données si vous ne l'utilisez pas – FabianCook

Répondre

0

Je n'ai jamais eu à utiliser le

cursor.close(); 

donc qui apporte la question de savoir comment vous gérez votre curseur et cela dépend vraiment sur lequel la version que vous visez pour votre application. Pour Honeycomb et plus tard je regarderais 'loader du curseur', et avant cela 'startManagingCursor' a été utilisé, mais je crois qu'il recommande aux chargeurs de curseur utilisateur, cela éliminera la nécessité pour vous de fermer le curseur. Comme le curseur est une tâche de type asynchrone, vous pouvez fermer le curseur avant de renvoyer des résultats.

pour les chargeurs de curseur Je suggère How to use loaders in Android

Vous pouvez également rechercher le code à github.com ou code.google.com, etc.

Hope this helps.

+0

Toujours utiliser cursor.close, il n'aurait pas une méthode proche si vous n'étiez pas destiné à l'utiliser, les méthodes close/end sont comme les destructeurs Java, parce que vous ne pouvez pas avoir de destructeurs en Java, vous devez utiliser une méthode pour spécifier quand se débarrasser des connexions etc, par exemple une base de données sqlite, quand vous utilisez les helpers vous devez fermer SQLiteDatabase quand vous avez fini . – FabianCook

+0

En outre, son code n'est pas asynchrone, ce qui ne l'affecte pas. – FabianCook

+0

Si vous utilisez un Loader Cursor, ne le gère-t-il pas pour vous (et oui il appelle indirectement le curseur) et n'êtes-vous pas censé garder les activités de base de données hors du thread UI? –

0

Im deviner que c'est à l'intérieur d'un SQLiteOpenHelper, ce que je suggère, pour éviter les serrures, est d'utiliser getWritableDatabase au lieu de getReadableDatabase, et au lieu de maintenir un objet de base de données pour la durée de l'activité/objet/ce qui est d'avoir un quelques méthodes qui gère tout cela.

par exemple:

private SQLiteDatabase mWritable; 

public SQLiteDatabase getDatabase(){ 
    if(mWritable == null) 
     mWritable = this.getWritableDatabase(); 
    if(mWritable.isOpen()) 
     return mWritable; 
    else 
     mWritable = this.getWritableDatabase(); 
    return mWritable; 
} 

public void start(){ 
    mWritable = getDatabase(); 
} 

public void stop(){ 
    if(mWritable != null) 
     if(mWritable.isOpen()) 
      if(!mWritable.inTransaction()) 
       mWritable.close(); 
} 

public void end(){ 
    if(mWritable == null) 
     return; 
    if(mWritable.isOpen()){ 
     if(mWritable.inTransaction()) 
      mWritable.endTransaction(); 
     mWritable.close(); 
    } 
} 

Cela peut prendre en charge la gestion de la base de données. Je suggère également d'ajouter d'autres méthodes à votre aide ainsi, ce qui peut aider ...

public Cursor Query(String query, String ... params){ 
    this.startReading(); 
    Cursor c = mWritable.rawQuery(query, params); 
    return c; 
} 

public String GetScalar(String query, String ... params){ 
    String res = null; 
    this.startReading(); 
    Cursor c; 
    try { 
     c = Query(query, params); 
     if(c.moveToFirst()){ 
      if(!c.isNull(0)){ 
       res = c.getString(0); 
      } 
     } 
     c.close(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    this.stopReading(); 
    return res; 
} 

public String GetScalar(String query){ 
    return GetScalar(query, new String[0]); 
} 

public String[] GetColumn(String query, String ... params){ 
    List<String> list = new ArrayList<String>(); 
    this.startReading(); 
    Cursor c; 
    try { 
     c = Query(query, params); 
     while(c.moveToNext()){ 
      if(!c.isNull(0)){ 
       list.add(c.getString(0)); 
      } 
     } 
     c.close(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    this.stopReading(); 
    return list.toArray(new String[0]); 
} 

public String[] GetColumn(String query){ 
    return GetColumn(query, new String[0]); 
} 

Je suggère que vous copiez la base de données de l'appareil, soit en utilisant l'application pour copier le db, ou si votre application navigue jusqu'à data/data/<your package name>/databases/<database name> et utilise SQLiteDatabaseBrowser (http://sourceforge.net/projects/sqlitebrowser/) pour le parcourir et exécuter la requête en utilisant ceci et vérifier les résultats.

Si vous devez copier la base de données que vous avez pas accès root, vous pouvez utiliser quelque chose le long des lignes de:

public static boolean CopyDatabase(DbHelper dbHelper) { 
    try { 
     File sd = Environment.getExternalStorageDirectory(); 
     File data = Environment.getDataDirectory(); 

     if (sd.canWrite()) { 
      String currentDBPath = "//data//" + dbHelper.getPackageName() 
        + "//databases//" + DbHelper.DATABASE_NAME; 
      String backupDBPath = "/Databases/"; 

      Log.d("Path", sd.getPath() + backupDBPath); 
      File backupDir = new File(sd.getPath() + "/" + backupDBPath); 
      File currentDB = new File(data, currentDBPath); 

      if (!backupDir.exists()) { 
       if (!backupDir.mkdirs()) { 
        Log.d("Path", "Can not make directory"); 
       } 
      } else { 
       Log.d("Path", "Directory exists"); 
      } 

      File backupDB = new File(backupDir, DbHelper.DATABASE_NAME); 

      FileInputStream fis = new FileInputStream(currentDB); 
      FileOutputStream fos = new FileOutputStream(backupDB); 
      FileChannel src = fis.getChannel(); 
      FileChannel dst = fos.getChannel(); 
      dst.transferFrom(src, 0, src.size()); 
      src.close(); 
      dst.close(); 
      fis.close(); 
      fos.close(); 
      return true; 
     } 
     Log.e("DbCopy", "Can not write to sdcard"); 
     return false; 
    } catch (Exception e) { 
     e.printStackTrace(); 
     return false; 
    } 
}