web-dev-qa-db-fra.com

Android: impossible d'effectuer cette opération car le pool de connexions a été fermé

Je lisais stackoverflow à propos de cette question et je n'ai toujours pas trouvé de solution. Je remarque que parfois, mon application lève cette erreur:

   Java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
        at Android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.Java:962)
        at Android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.Java:599)
        at Android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.Java:348)
        at Android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.Java:894)
        ...

J'ai un fichier appelé DatabaseHelper.Java utilisant cette approche pour en obtenir une instance:

public static DatabaseHelper getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new DatabaseHelper(context.getApplicationContext());
    }
    return mInstance;
}

Ensuite, j'ai des méthodes comme celle-ci (qui s'est écrasé dans la ligne cursor.moveToFirst () avec cette erreur). Il ne se bloque presque jamais, mais parfois il le fait.

public Profile getProfile(long id) {
    SQLiteDatabase db = this.getReadableDatabase();

    String selectQuery = "SELECT * FROM " + TABLE_PROFILES + " WHERE " + KEY_PROFILES_ID + " = " + id;
    Cursor cursor = db.rawQuery(selectQuery, null);

    // looping through all rows and adding to list
    Profile profile = new Profile();
    if (cursor.moveToFirst()) {
        doWhatEver();
    }
    cursor.close();
    db.close();

    return profile;
}

Voilà donc, dans toutes les méthodes que j'utilise:

SQLiteDatabase db = this.getReadableDatabase(); (or Writable)

Et puis je ferme le curseur et la base de données. Dans ce cas, l'erreur a jeté dans la ligne:

cursor.moveToFirst();

Je ne vois pas pourquoi l'erreur indique que la base de données est fermée si j'appelle this.getReadableDatabase () auparavant. Soutenez nous s'il vous plaît! Je vous remercie :)

53
Ferran Negre

Retirer

db.close();

Si vous essayez une autre opération après la fermeture de la base de données, cette exception vous sera fournie.

Le documentation dit:

Libère une référence à l'objet, en fermant l'objet ...

En outre, consultez exception fermée pour Android Sq Lite à propos d'un commentaire d'un ingénieur Framework Android qui indique qu'il n'est pas nécessaire de fermer la connexion à la base de données.

77
AmmarCSE

J'ai actuellement le même problème. Tout en supprimant db.close () pour résoudre le problème pour moi, je pense que le problème est causé par le multi-threading. Voici mes recherches.

SQLiteOpenHelper contient une référence à SQLiteDatabase. Lorsque getReadableDatabase () ou getWritableDatabase () est appelée, elle renvoie la référence. Si le SQLiteDatabase est fermé ou null, un nouvel objet SQLiteDatabase est créé. Notez que dans la méthode get, les codes sont gardés dans un bloc synchronisé.

SQLiteDatabase est une sous-classe de SQLiteClosable. SQLiteClosable implémente un schéma de comptage de références.

  • Lorsqu'il est créé pour la première fois, le nombre est 1.

  • Lorsque les méthodes d'opération de base de données sont exécutées (comme insertion, requête), le nombre augmente et diminue le nombre lorsque les méthodes se terminent. Mais les opérations du curseur ne sont PAS protégées par le comptage de références.

  • Si le nombre diminue à 0, le pool de connexions sera fermé et un objet SQLiteConnectionPool membre sera défini sur null, et la base de données SQLite est maintenant fermé;

  • SQLiteDatabase.close () diminue le nombre de 1;

Donc, si vous avez un schéma à un seul thread, la fermeture de SQLiteDatabase suffira, car SQLiteOpenHelper va simplement le recréer.

Si vous utilisez le multi-threading, vous rencontrerez des problèmes. Dites que les threads A et B appellent getReadableDatabase (), et SQLiteOpenHelper renvoie le SQLiteDatabase qu’il détient, puis le thread A termine d’abord son opération et appelle SQLiteDatabase.close (), le thread d’objet SQLiteDatabase B étant alors fermé donc tous les appels d'opération de base de données ou de méthode de curseur ultérieurs renverront l'exception.

19
handhand
//close database after cursor is closed like:

if(cursor.getCount() != 0){

    while(cursor.moveToNext()){
      //do operation
    }
}

cursor.close();
database.close();
3
Mohammad nabil

J'ai le même problème, je n'ai pas pu le résoudre. J'ai trouvé un indice possible: j'ai un fil de synchronisation qui tourne tout le temps:

    Item ii = dbHelper.popPendingUpload();
    if (ii != null)
            upload(ii);

Et à l'intérieur de DBHelper

public Item popPendingUpload() {

    SQLiteDatabase db = getReadableDatabase();
    Cursor res = db
            .rawQuery("SELECT * FROM items WHERE state = 0 LIMIT 1",
                    new String[] {});
    boolean hasNext = res.moveToFirst();
    Item ii = null;
    if (hasNext) {
        ii = //load item
    }
    db.close();
    return ii;
}

L'erreur apparaît également dans l'appel à la méthode moveToFirst (). Comme le fil dépose les éléments dans une boucle infinie, la première fois que cela fonctionne, la deuxième fois, l'erreur apparaît. La partie intéressante est que si je mets un point d'arrêt et passe le code, l'erreur ne s'affiche plus. Je teste sur un appareil réel en utilisant Android 4.1.

Je sais, n'est pas une réponse, mais cela peut aider. Je vais continuer à tester.

3
Jason Oviedo

Ceci est une simple erreur dans Android initialisation de l'architecture de la base de données de pièces avec getApplicationContext (), cela signifie que votre application possède une instance de la référence de base de données, quel que soit le nombre d'activités en créant une instance, Par conséquent, si vous le fermez dans une activité, les autres lèveront cette exception.

Essentiellement, ne vérifiez pas seulement l'activité en lançant l'exception, mais toutes les activités pour db.close ()

1
E-max

J'ai une erreur comme la tienne, voici mon code:

  try {
                        String sql = "SELECT * FROM "+DB_TABLEDOWNLOAD;
                        Cursor cursor = db.rawQuery(sql, null);
                        //空双引号为原表没有的字段
                        String temp = "";
                        int existIconPath = cursor.getColumnIndex("iconPath");
                        int existAudioID = cursor.getColumnIndex("audioID");

                        if (existIconPath == -1 && existAudioID == -1){
                            temp = "url, downed,total,path,name, audioTitle, audioFileID,\"\",\"\"";
                        }else if (existIconPath == -1 && existAudioID != -1){//iconPath不存在
                            temp = "url, downed,total,path,name, audioTitle, audioFileID,\"\",audioID";
                        }else if (existIconPath != -1 && existAudioID == -1){//audioID不存在
                            temp = "url, downed,total,path,name, audioTitle, audioFileID,iconPath,\"\"";
                        }else {
                            return;
                        }

                        db.beginTransaction();
                        String tempTableName = "_temp_"+DB_TABLEDOWNLOAD;
                        String sqlCreateTemp = " ALTER TABLE "+DB_TABLEDOWNLOAD+" RENAME TO "+tempTableName+";";
                        db.execSQL(sqlCreateTemp);

                        final String TB_TESTPAPERINFO_CREATE = "Create  TABLE IF NOT EXISTS "
                                + DB_TABLEDOWNLOAD
                                + "(url TEXT, downed TEXT,total TEXT,path TEXT,name TEXT, audioTitle TEXT, audioFileID TEXT,iconPath TEXT, audioID TEXT);";
                        db.execSQL(TB_TESTPAPERINFO_CREATE);

                        String sqlBackupData = "INSERT INTO "+DB_TABLEDOWNLOAD+" SELECT "+temp+" FROM "+tempTableName+";";
                        db.execSQL(sqlBackupData);
                        String sqlDrop = "DROP TABLE IF EXISTS '"+tempTableName+"';";
                        db.execSQL(sqlDrop);
                        db.setTransactionSuccessful();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally{
                        db.endTransaction();
                    }

J'utilise return avant db.beginTransaction (), mon code retourne avant beginTransaction, mais je finis par EndTransaction. si vous ne commencez pas et ne terminez pas, l’exception apparaîtra.

alors vérifiez votre code sur db.beginTransaction et endTransaction.

0
doordoor

Peut-être fermez-vous la base de données avant d'accéder à la base de données à partir de votre application.

Vous devez modifier getProfile () pour

public Profile getProfile(long id) {
  SQLiteDatabase db = this.getReadableDatabase();

  try {
    String selectQuery = "SELECT * FROM " + TABLE_PROFILES + " WHERE " + KEY_PROFILES_ID + " = " + id;
    Cursor cursor = db.rawQuery(selectQuery, null);

    // looping through all rows and adding to list
    Profile profile = new Profile();
    if (cursor.moveToFirst()) {
      doWhatEver();
    }
    cursor.close();
  finally {
    db.close();
  }
  return profile;
}
0
yaginier