【问题标题】:Attempt to reopen an already-closed object尝试重新打开已经关闭的对象
【发布时间】:2014-10-10 14:11:10
【问题描述】:

我有一个数据库助手类和三个数据源类,用于同一数据库中的三个表。 通过 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() 给另一个正在运行的线程带来麻烦。

因此寻找解决方案来避免这种并发访问问题。 谢谢。

【问题讨论】:

  • requestsOpenvolatile 字段吗?
  • @PedroOliveira 不,但我想使用同步方法会在这里产生 volatile 的影响。
  • 我不确定。但是由于您可以同时调用 close 和 open(因为它们是相互依赖的同步的),因此不同的任务可以同时更改值。为什么不保留一个在单例中打开的数据库实例并从异步任务中访问它?
  • @PedroOliveira 我将不得不更改很多现有代码,我在某种程度上也尝试过,我保持 DBHelper 单调,将方法打开和关闭从数据源类移到它,但是在调用 dbhelper.close (),它本身递归调用,导致溢出,花了很多时间检查原因,但不能。所以实现了这个逻辑。
  • @PedroOliveira 我正在考虑在一个同步函数中组合以上 3 件事 1)newOpenRequest() 2)canClose() 3)我将在检查 `requestsOpen 后立即关闭同一函数中的数据库连接== 0。你认为这会有所帮助吗?

标签: android sqlite


【解决方案1】:

我学会了永远不要在 android 中关闭数据库。所以也许你的解决方法是不关闭数据库。没有意义,在您的应用程序的整个生命周期中保持打开状态。当您的应用 id 被销毁时,Android 将释放资源。

您不需要同步数据库调用,因为 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 帮助器的示例 - 关闭游标而不是 db

SQLiteDatabase db = DBOpenHelper.getInstance(context).getWritableDatabase();

    Cursor cursor = null;
    try {
      cursor = db.query...

    }

    finally {
        cursor.close();

    }

【讨论】:

  • 是的,我读到它对于同一个连接池来说是线程安全的,但由于只使用 close ,我遇到了问题。保持连接打开真的是个好主意吗?因为我的应用程序还使用服务来处理来自服务器的传入推送消息,该服务器再次访问数据库,所以我的连接将永远保持打开状态。
  • 我有很多安卓应用在生产中,我们没有关闭数据库。如果卸载应用程序,操作系统会释放对 db 文件的锁定。打开和关闭是不必要的开销,并会导致您所描述的问题。这篇文章:stackoverflow.com/questions/4557154/… 谈到了在一些 onDestroy 生命周期方法上关闭它。我在这里学到的教训是,打开的连接并不是一件坏事,它只是一个文件上的锁。永远保持开放恕我直言。
猜你喜欢
  • 1970-01-01
  • 2012-09-28
  • 1970-01-01
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多