我在多线程应用程序中遇到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,因为无法使用来自多个线程的数据库,但现在我实现了第一个后台用例,所有当然,地狱破裂了。实际上,似乎可以同时打开多个连接,只要它们不被同时访问,因为我有许多活动堆叠在一起,每个都有自己的帮助器,并且(错误的?)没有在onPause中关闭它们并在onResume中打开。
所以,问题是我在这里做了什么从根本上做错了什么?基于我的研究,似乎只有两个解决方案:创建一个ContentProvider来管理数据库(我不想被迫这样做,因为我真的不需要CP),或者以某种方式只保留一个整个应用程序的DbHelper /连接。我是如何做到这一点并不重要,但截至目前,我最喜欢的可能是在自定义Application子类中初始化的单例(yuck)。在将来我想试试Dagger,所以我可能会把它变成@Singleton,但还没有。
编辑:,遗憾的是,我的ui仍然阻止 - 导入器事务阻止了ui初始化方法的任何数据库读取。我想我需要找到一种方法来启动读取器事务,这些事务不会被单个写入tx阻塞。
编辑2:我能够在作家工作时成功解锁阅读器。要做到这一点:
我致电
setWriteAheadLoggingEnabled(true);
在DbHelper构造函数
使用
db.beginTransactionNonExclusive();
而不是
db.beginTransaction();
在ImporterLoader(执行写入的东西)