我有一个数据库助手类和三个数据源类用于同一个数据库中的三个表。
通过AsyncTasks可以在很多地方访问数据库。我遇到了“尝试重新打开一个已经关闭的对象......”的问题,我四处搜索并发现dbhelper.getReadableDatabase()
返回已打开连接的同一对象。我猜测问题必定是因为两个线程同时执行操作并且其中一个线程完成其任务并调用close()
连接关闭并且运行线程抛出此异常。
所以为了避免close()
我写了两个方法:
public static synchronized void newOpenRequest() {
requestsOpen++;
Util.debuglog(TAG, "Open requests: " + requestsOpen);
}
public static synchronized boolean canClose() {
requestsOpen--;
Util.debuglog(TAG, "Open requests: " + requestsOpen);
if(requestsOpen == 0)
return true;
return false;
}
在所有三个数据源类中,当我按以下方式执行时:
private void openRead() {
database = dbhelper.getReadableDatabase();
DBHelper.newOpenRequest();
Log.i(TAG, "Database opened.");
}
private void openWrite() {
database = dbhelper.getWritableDatabase();
DBHelper.newOpenRequest();
Log.i(TAG, "Database opened.");
}
private void close() {
if (DBHelper.canClose()) {
dbhelper.close();
Util.debuglog(TAG, "Database closed.");
}
}
我的LogCat输出如下:
因此,在黑色矩形中突出显示,总openRequests
为0,因此数据库已关闭,正常但在红色矩形中突出显示,
首先openRequests
为0,所以只有时间才能关闭数据库,但是(我的猜测)发生的事情是canClose()
为一个线程返回true,就在调用dbhelper.close();
另一个之前名为open()
的线程(因为openRequests = 1在关闭之前就在LogCat上)然后第一个线程的close()
被调用给另一个正在运行的线程带来麻烦。
因此寻找解决方案以避免此并发访问问题。 谢谢。
答案 0 :(得分:1)
我已经学会了永远不要在android中关闭数据库。所以也许你的修复是不关闭数据库。没有必要,在应用程序的整个生命周期中保持打开状态。 Android将在您的应用ID被销毁时释放该资源。
您不需要同步数据库调用,因为sqlite可以是线程安全的。
Is Sqlite Database instance thread safe
DBOpenHelper工作正常:
public class DBOpenHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 31;
private static DBOpenHelper mInstance;
private static final String DATABASE_NAME = "thedb.db";
public static DBOpenHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new DBOpenHelper(context.getApplicationContext());
}
return mInstance;
}
private DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
使用db helper的示例 - 关闭游标但不关闭db
SQLiteDatabase db = DBOpenHelper.getInstance(context).getWritableDatabase();
Cursor cursor = null;
try {
cursor = db.query...
}
finally {
cursor.close();
}