je reçois une erreur "Finalisation d'un curseur qui n'a pas été désactivé ou fermé" sur ce morceau de code. Le code est utilisé pour remplir une vue de liste.
Puisqu'il s'agit d'une erreur non fatale, il n'y a pas de plantage et tout semble bien fonctionner ... mais je n'aime pas l'erreur.
Si je ferme le curseur à la fin de ce code, la liste reste vide. si je ferme le curseur dans onStop, j'obtiens la même erreur.
Comment puis-je réparer ça??
private void updateList() {
DBAdapter db = new DBAdapter(this);
db.open();
//load all waiting alarm
mCursor=db.getTitles("state<2");
setListAdapter(new MyCursorAdapter(this, mCursor));
registerForContextMenu(getListView());
db.close();
}
error :
E/Cursor ( 2318): Finalizing a Cursor that has not been deactivated
or closed. database = /data/data/xxxxxxxxxxxxxxx.db, table = alerts,
query = SELECT _id, alert_id,
E/Cursor ( 2318):
Android.database.sqlite.DatabaseObjectNotClosedException: Application
did not close the cursor or database
object that was opened here
E/Cursor ( 2318): at
Android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.Java:210)
E/Cursor ( 2318): at
Android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.Java:
53)
E/Cursor ( 2318): at
Android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:
1345)
E/Cursor ( 2318): at
Android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.Java:
1229)
....
....
Vous ne devriez pas recevoir ce message si vous fermez le Cursor
dans onStop()
ou onDestroy()
. Veuillez réessayer. Ou, appelez startManagingCursor()
après avoir obtenu le Cursor
de votre requête, et Android fermera le Cursor
de lui-même.
Scott,
J'ai rencontré le même problème que toi. Avant de fermer votre base de données, c'est-à-dire "db.close ()", assurez-vous que vos curseurs sont fermés en premier, c'est-à-dire "mCursor.close ()"
Ainsi:
private void updateList()
{
DBAdapter db = new DBAdapter(this);
db.open();
//load all waiting alarm
mCursor=db.getTitles("state<2");
setListAdapter(new MyCursorAdapter(this, mCursor));
registerForContextMenu(getListView());
// Let's close the cursor.
mCursor.close();
db.close();
}
Vous avez mentionné que si vous fermiez votre curseur, votre affichage de liste reste vide. Je vous recommande de passer les informations à une classe et de les copier (allouer la mémoire) puis de fermer le curseur.
Lorsqu'une requête renvoie un curseur, il est en fait positionné "avant" le premier enregistrement du curseur. Un adaptateur tentera de faire un "getItem" sur le premier élément afin qu'il échoue car le curseur n'est positionné à aucun.
Dans mes adaptateurs de base, je fais un cursorMoveToPosition sur les getViews. Cela semble éliminer le besoin de se déplacer en premier.
N'utilisez pas startManagingCursor () car ce n'est plus l'approche recommandée. Le problème se produit car une connexion curseur/base de données n'est toujours pas fermée au moment où le finaliseur atteint cet objet. Vous pouvez éviter cela en autorisant un chargeur à gérer le curseur ou en suivant vous-même toutes les connexions curseur/DB/SQLiteOpenHelper et en les nettoyant.
L'utilisation d'un chargeur est assez lourde et nécessite beaucoup de pièces mobiles pour fonctionner conjointement avec, par exemple, une vue de liste. D'un autre côté, le suivi de vos connexions de curseur et de base de données est sujet à des erreurs humaines. Si le nombre d'objets curseur/DB est faible, je recommanderais cette dernière solution. Sinon, laissez un chargeur gérer vos connexions.
Fermez l'objet curseur là où vous le créez.
Lorsque vous créez un objet curseur et que vous avez terminé de parcourir une table SQLite, fermez-le après l'avoir utilisé. Cette fermeture du curseur empêche toute exception dans logcat.
Vous n'obtiendrez aucune exception liée à la finalisation du curseur laissé ouvert.
Ce problème fixe même dans ma demande.
J'ai juste eu le même problème et pensé à vous le faire savoir - juste au cas où ...
J'ai accidentellement appelé ma routine de récupération deux fois et j'ai "perdu" le curseur résultant du premier appel. Cela a provoqué l'erreur.
Moi aussi, j'ai eu des problèmes avec la fermeture du curseur:
La fermeture du curseur juste après avoir défini l'adaptateur de la vue liste provoque la fermeture du curseur avant l'affichage des données.
Impossible d'utiliser startManagingCursor pour gérer le curseur car il est obsolète.
Le nouveau remplacement de cursorLoader pour startManagingCursor semble être excessif.
Déplacer la position du curseur comme suggéré n'a pas fonctionné.
Faire de la tâche une classe interne de l'activité et fermer le curseur dans la méthode onDestroy de l'activité fonctionne parfois mais pas tout le temps.
Faire de la tâche une classe interne de l'activité et fermer le curseur dans la méthode onStop de l'activité semble fonctionner.
J'ai également découvert que je pouvais fermer la base de données et l'assistant d'ouverture sqlite avant de fermer le curseur. Je peux même les fermer juste après avoir configuré l'adaptateur de la vue liste. Les données s'affichent toujours.
startManagingCursor (curseur);
Cela a résolu mon problème
J'ai lutté avec ce problème pendant deux jours. J'essayais d'obtenir un exemple de code qui passait un curseur, renvoyé d'une requête de base de données, directement à l'adaptateur de liste - pas d'adaptateur provisoire. Il a refusé de fonctionner - juste affiché un écran vide - jusqu'à ce que j'appelle 'moveToFirst ()' sur le curseur avant de le passer à ListAdapter. Allez comprendre! Quand je commente cela, ça casse.
Je pensais juste que je partagerais cela pour sauver les gens des mêmes difficultés que moi.
Si quelqu'un peut nous expliquer pourquoi il en est ainsi, je l'apprécierais. Je n'ai pas eu à invoquer moveToFirst sur les curseurs jusqu'à présent, pour les faire fonctionner correctement.