Exception: tenter d’acquérir une référence à proximité de SQLiteClosable

Je l’ai posté en mai sur le groupe Google [android développeurs]. Je n’ai jamais eu de réponse et je n’ai pas réussi à reproduire le problème avant qu’un de mes étudiants ne le fasse la semaine dernière. Je pensais que je le posterais ici et verrais si ça sonnait des cloches pour quiconque.

Dans l’un de mes exemples de code, j’ai la méthode suivante:

static Cursor getAll(SQLiteDatabase db, Ssortingng orderBy) { return(db.rawQuery("SELECT * FROM restaurants "+orderBy, null)); } 

Quand je le lance, de façon sporadique, je comprends ceci:

 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): java.lang.IllegalStateException: attempt to acquire a reference on a close SQLiteClosable 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:31) 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:56) 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at android.database.sqlite.SQLiteQuery.(SQLiteQuery.java:49) 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49) 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1118) 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1092) 05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at apt.tutorial.Restaurant.getAll(Restaurant.java:14) 

Cela n’a aucun sens pour moi. La firebase database est définitivement ouverte. Le SQLiteClosable est le SQLiteQuery créé par SQLiteQueryDriver , et je ne vois aucune preuve qu’il existe un pool d’objects ou quelque chose qui pourrait expliquer comment un “nouveau” SQLiteClosable est déjà fermé. Le fait qu’il soit sporadique (ce qui signifie que les mêmes opérations d’interface utilisateur déclenchent parfois l’exception, mais pas toujours) suggère une sorte de pool, une condition de course ou quelque chose … mais je ne sais pas où.

Pensées?

Merci!

MISE À JOUR : Le code en question provient des didacticiels LunchList de mon livre de tutoriels de programmation Android . C’est un peu étalé et pas très approprié pour poster directement dans SO. Vous pouvez télécharger le code de ce livre à partir du lien ci-dessus si vous souhaitez le consulter. Je ne me souviens plus exactement de l’édition du tutoriel sur laquelle l’étudiant travaillait à l’époque, bien qu’il se trouvait dans la série Tutoriel 12-Tutorial 16. J’espérais surtout rencontrer quelqu’un qui avait trébuché sur ce problème auparavant et qui avait probablement un coupable. Je suis presque certain que ma firebase database est ouverte. Merci encore!

Celui-ci m’a rendu fou le plus longtemps. La solution que j’ai trouvée est assez simple: ne conservez pas de références aux objects SQLiteDatabase . Au lieu de cela, utilisez un SQLiteOpenHelper et appelez getWritableDatabase() chaque fois que vous en avez besoin. De la documentation:

SQLiteDatabase public synchronisée getWritableDatabase ()

Créez et / ou ouvrez une firebase database qui sera utilisée pour la lecture et l’écriture. Une fois ouverte avec succès, la firebase database est mise en cache, vous pouvez donc appeler cette méthode chaque fois que vous avez besoin d’écrire dans la firebase database.

La réponse était là tout le temps.

SQLiteDatabase est fermée automatiquement sous certaines conditions.

http://darutk-oboegaki.blogspot.com/2011/03/sqlitedatabase-is-closed-automatically.html

Les méthodes getCount () et onMove () de Cursor déclenchent une requête réelle à l’aide de SQLiteQuery. Une fois toutes les données requirejses obtenues, SQLiteQuery décrémente le nombre de références de l’instance SQLiteDatabase. Lorsque le nombre de références atteint 0, la firebase database est fermée.

Notez que la requête peut être exécutée de manière asynchrone et, dans ce cas, getCount () peut renvoyer -1 si elle est appelée avant que SQLiteQuery ait fini de préparer les données.

J’ai eu le même problème pendant quelques jours et ma solution a été de mettre la méthode open () juste avant la méthode query et close () après le fonctionnement de la firebase database. Cela ressemble à quelque chose comme ça.

 open(); Cursor cur=db.query(DATABASE_TABLE_CON, null, null, null, null, null, " name ASC"); close(); return cur; 

Cela fonctionne très bien, mais je m’inquiète du coût des ressources. Je ne suis pas sûr de dépenser plus de ressources avec toute cette firebase database d’ouverture et de fermeture avant toute action.

J’ai connu un problème similaire, même si je suivais déjà les conseils de Jarett . Dans mon cas, le problème se pose assez régulièrement pour les changements d’orientation. J’ai découvert que, pour une raison quelconque, je ne suis pas encore au bout de mes peines, mon code génère deux AsyncTasks identiques et quasiment simultanés lors d’un changement d’orientation (par opposition à un seul lorsque l’activité démarre normalement). Ces tâches effectuent la même requête de firebase database en même temps à partir de différents threads.

Cette exception (ou parfois une autre exception SQLiteException) est le résultat. Il semble donc que ce message peut être un symptôme de problèmes de concurrence, même si ce n’est pas nécessairement la racine du problème initial affiché ici.