SQLite有多个并发打开连接的麻烦

时间:2015-02-07 09:39:40

标签: android android-sqlite sqliteopenhelper

我在多线程应用程序中遇到SQLite问题。我有一个Activity,它使用AsyncTaskLoader的子类从文件(由Uri指定,因为它来自Android存储访问框架)执行一些数据导入,并且当加载器启动并完成其工作(它写入数据库)并且设备被旋转,我得到一个' android.database.sqlite.SQLiteDatabaseLockedException:数据库被锁定(代码5)'例外。我知道问题是什么(多个SQLiteOpenHelpers访问数据库),但我不确定我应该如何解决这个问题。这是我的代码:

的活动:

private void importTests(Uri uri) {
    Bundle loaderArgs = new Bundle();
    loaderArgs.putParcelable(URI_IMPORTER_LOADER_ARG, uri);
    getLoaderManager().initLoader(0, loaderArgs, this).forceLoad();
}

@Override
public Loader<Exception> onCreateLoader(int id, Bundle args) {
    Uri uri = args.getParcelable(URI_IMPORTER_LOADER_ARG);
    return new ImporterLoader(this, uri, dbHelper);
}

@Override
public void onLoadFinished(Loader<Exception> loader, Exception exception) {
    getLoaderManager().destroyLoader(loader.getId());

    if (exception != null) {
        // import failed, show toast
    } else {
        // init Ui
    }
}

@Override
public void onLoaderReset(Loader<Exception> loader) {
    // nothing
}

(注意:我不会在onCreate中创建Loader;相反,我会在调用导入功能时按需执行,并在准备好后立即将其销毁。我不确定是否这是使用加载器的正确方法。)

现在有关该问题的更多详细信息:

  • 当活动开始时,它会创建一个DbHelper(它是一个SQLiteOpenHelper子类),将它存储在一个成员字段中,并读取数据库(使用getReadableDatabase(),但很可能它仍然是可写的)来初始化Ui(显示项目列表等)
  • 如果触发导入,则会创建一个Loader并从活动中获取DbHelper;这个导入使用其InputStreams读取一个Uri,对数据库进行一些解析和写入行
  • 在导入期间,设备旋转时,会发生以下情况:加载程序仍在继续(我不想破坏它,我希望它完成任务并触发更新Ui时#&# 39;完成了,可能是在新的,旋转的活动中 - 这就是我使用Loader的原因),它仍然使用它的DbHelper实例及其开放连接;然而,活动被销毁,然后再次创建,创建另一个DbHelper实例,它再次尝试读取数据库以初始化Ui
  • 因此,有两个DbHelper,每个都有一个打开的连接,第二个来自新活动实例,从getReadableDatabase()
  • 抛出SQLiteDatabaseLockedException

到目前为止,我一直在应用中的每个活动中使用新的DbHelper,因为无法使用来自多个线程的数据库,但现在我实现了第一个后台用例,所有当然,地狱破裂了。实际上,似乎可以同时打开多个连接,只要它们不被同时访问,因为我有许多活动堆叠在一起,每个都有自己的帮助器,并且(错误的?)没有在onPause中关闭它们并在onResume中打开。

所以,问题是我在这里做了什么从根本上做错了什么?基于我的研究,似乎只有两个解决方案:创建一个ContentProvider来管理数据库(我不想被迫这样做,因为我真的不需要CP),或者以某种方式只保留一个整个应用程序的DbHelper /连接。我是如何做到这一点并不重要,但截至目前,我最喜欢的可能是在自定义Application子类中初始化的单例(yuck)。在将来我想试试Dagger,所以我可能会把它变成@Singleton,但还没有。

编辑:,遗憾的是,我的ui仍然阻止 - 导入器事务阻止了ui初始化方法的任何数据库读取。我想我需要找到一种方法来启动读取器事务,这些事务不会被单个写入tx阻塞。

编辑2:我能够在作家工作时成功解锁阅读器。要做到这一点:

  • 我致电

    setWriteAheadLoggingEnabled(true);
    

    在DbHelper构造函数

  • 使用

    db.beginTransactionNonExclusive();
    

    而不是

    db.beginTransaction();
    

    在ImporterLoader(执行写入的东西)

0 个答案:

没有答案