Android:由于连接池已closures,无法执行此操作
我正在通过关于这个问题的stackoverflow阅读,我还没有find一个解决scheme。 我注意到有时,我的应用程序抛出这个错误:
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) ...
我有一个名为DatabaseHelper.java的文件,使用这种方法来获取它的一个实例:
public static DatabaseHelper getInstance(Context context) { if (mInstance == null) { mInstance = new DatabaseHelper(context.getApplicationContext()); } return mInstance; }
然后我有像这样的方法(它崩溃的行cursor.moveToFirst()与该错误)。 它几乎从来没有崩溃,但有时它确实。
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; }
就这样,在我使用的所有方法中:
SQLiteDatabase db = this.getReadableDatabase(); (or Writable)
然后closures游标和数据库。 在这种情况下,错误被排除在外:
cursor.moveToFirst();
我不明白为什么错误说db是closures的,如果我之前调用this.getReadableDatabase()。 请支持! 谢谢 :)
去掉
db.close();
如果在closures数据库之后尝试另一个操作,它会给你这个exception。
该文件说:
释放对象的引用, closures对象…
此外,请查看Android Sq Liteclosures的exception,以了解Android Framework工程师的意见,该意见声明closures数据库连接并不是必需的。
我目前有同样的问题。 虽然删除db.close()解决了我的问题,我认为这个问题是由multithreading引起的。 这是我的研究。
SQLiteOpenHelper持有对SQLiteDatabase的引用,当调用getReadableDatabase()或getWritableDatabase()时,它将返回引用,如果SQLiteDatabase被closures或为null,则会创build一个新的SQLiteDatabase对象。 请注意,在get方法中,代码被保护在一个同步块中。
SQLiteDatabase是SQLiteClosable的一个子类。 SQLiteClosable实现了一个引用计数scheme。
-
首次创build时,计数为1。
-
当数据库操作方法运行(如插入,查询)时,会增加计数,减less方法结束时的计数。 但是游标操作不受引用计数的保护。
-
如果计数减less到0,连接池将被closures,并且成员SQLiteConnectionPool对象将被设置为空,现在SQLiteDatabase被closures ;
-
SQLiteDatabase.close()会减less1;
所以,如果你有一个单线程scheme,closuresSQLiteDatabase将会很好,因为SQLiteOpenHelper将会重新创build它。
如果你做multithreading,那么你会遇到麻烦。 说线程A和线程B都调用getReadableDatabase(),并且SQLiteOpenHelper返回它持有的SQLiteDatabase,然后线程A首先完成它的操作并调用SQLiteDatabase.close(),现在SQLiteDatabase对象线程B已经closures,所以后面的任何数据库操作调用或游标方法调用将抛出exception。
我有同样的问题,一直无法解决它。 我发现了一个可能的线索:我有一个正在运行的同步线程:
Item ii = dbHelper.popPendingUpload(); if (ii != null) upload(ii);
在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; }
在moveToFirst()方法调用中也会出现该错误。 当线程在无限循环中popup项目时,第一次工作正常,第二次出现错误。 有趣的部分是,如果我把一个断点,并通过代码一步,错误不再显示。 我在使用Android 4.1的真实设备上进行testing。
我知道,这不是一个答案,但可能有帮助。 我会继续testing。
//close database after cursor is closed like: if(cursor.getCount() != 0){ while(cursor.moveToNext()){ //do operation } } cursor.close(); database.close();
也许你在从应用程序访问数据库之前closures数据库。
你必须编辑getProfile()来
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; }
我有一个像你的错误,在这里我的代码:
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(); }
字段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(); }