ContentProvider抛出SQLiteCantOpenDatabaseException:无法打开数据库文件

时间:2013-11-25 09:53:25

标签: android sqlite android-sqlite android-contentprovider

在我的内容提供商中,我创建并维护了3个SQLiteDatabase个对象。它们是这样创建的:

    private ContentProviderHelper helper;

    @Override
    public boolean onCreate() { // that's the ContentProvider onCreate()
       SQLiteDatabase dbLog = new DbLog(getContext()).getWritableDatabase();
       SQLiteDatabase dbSession = new DbSession(getContext()).getWritableDatabase();
       SQLiteDatabase dbLocation = new DbLocation(getContext()).getWritableDatabase();

       helper = new ContentProviderHelper(UriManager.getAuthority(getContext()));
       helper.addDb(dbLog, DbLog.TABLE_NAME, UriManager.LOG, SQLiteDatabase.CONFLICT_REPLACE);
       helper.addDb(dbSession, DbSession.TABLE_NAME, UriManager.SESSION, SQLiteDatabase.CONFLICT_REPLACE);
       helper.addDb(dbLocation, DbLocation.TABLE_NAME, UriManager.LOCATION, SQLiteDatabase.CONFLICT_REPLACE);

ContentProviderHelper将SQLiteDatabaseArrayList存储在UriMatcher索引的<provider>中。

SQLiteCantOpenDatabaseException: unable to open database file已在清单中正确注册,我的应用已获得SD卡许可。

我们的大部分500.000用户群都运行良好,但每隔一段时间我就会从Google Play中获得Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file at android.database.sqlite.SQLiteDatabase.dbopen(Native Method) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1013) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986) at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1051) at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:787) at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:157)

相关的堆栈跟踪是:

Caused by: android.database.sqlite.SQLiteDiskIOException: disk I/O error: COMMIT;
at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1763)
at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:583)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:137)
at ***.***.***.data.ContentManager.onCreate(ContentManager.java:26)

这些错误报告通常来自通用的无品牌劣质设备。

非常感谢任何有关如何正确避免这些错误的帮助。

修改

我在同一代码上遇到的不同SQL异常。

try{} catch(){}

除了IntentService之外还有其他任何处理方法吗?

修改

有关游标使用的额外信息:

一般情况下,游标会在最短的时间内保持打开状态。所有3个SQLiteDatabase都有一个Wrapper,用于执行实际的Cursor调用,从中读取数据并关闭它们。只有一个实例将光标传递回调用对象,但它是一个{{1}},同样是单线程,单实例,单个进程(就像ContentProvider一样)循环游标并关闭它。 / p>

2 个答案:

答案 0 :(得分:2)

这看起来不像你的(Java)方面的编程错误,也不是Android SQLite包装类。

提供的堆栈跟踪和代码并没有提供太多信息来证明我的猜测,但我认为这应该是由于那些SDCard的不可靠性(你已经提到观察到的痕迹通常来自低质量手机)

异常本身是在本机sqlite代码中生成的,虽然我没有查找JNI接口的C / C ++部分,但这应该直接来自底层的sqlite3_open调用。由于非常明显的原因,执行错误代码不包含在抛出的异常中,所以你在这里找到根本原因基本上没有运气。

由于这直接来自本机层,因此它存在某种文件系统/硬件问题。卡可能坏了,卡片可能坏了/脏/其中任何东西或其中任何东西都可能搞砸(很可能是物理上的)。

为您提供解决问题的可能方案,您无法解决:不要使用(有时不可靠)SD卡。如果您的数据库足够小(少于几MB),您应该将它们存储在内部闪存上。这必须是可靠的存储,就好像写入或查询失败,用户无论如何都会有更严重的问题。

如果这不是一个选项,你可以写&#34;最新数据&#34; (我不知道你在哪里存储哪种数据)并在SDCard上迁移这个数据。这样你至少可以尝试存储它而不用try / catch来填充你的代码(如果你在一个单独的线程中尝试这个,它会捕获顶层的任何异常),但是它并没有解决你的读取部分。问题

另一个,虽然有点难看,但我可以想象:只是让异常直接通过并在Application类中捕获它们(你可以重载它),将它推送到磁盘(内部,显然)并重新抛出它以使其崩溃你的应用。在下一次启动时,您可以查找是否已被SQLite异常崩溃并向用户显示一个msg,其中包含类似&#34;抱歉,您的SD卡似乎已被破坏,因为我们无法解决用它。考虑购买新的订购更好的设备&#34;。 ACRA可以帮助你解决这个问题,它是我上一次使用它(1年前)时尝试描述得很好的。

答案 1 :(得分:0)

我认为你在并发方面存在一些问题,并且你试图不止一次地获得DB对象。 此外 - 具有3个不同的DB(可能)使应用程序一次运行3个DB引擎(内存问题)。 没有单一且简单的解决方案,而问题位于异常抛出的其他位置,但您可以执行以下步骤: 将3个数据库放入一个文件中(如果可能)。 创建一个简单的单例以将DB对象保留在内存中,并避免对存储文件的并发尝试。请记住,SQLite声称为“线程安全”而不是“多线程”,因此IO错误可能只是保护DB损坏的自然行为。 确保游标尽快关闭(即在查询对象后读取光标并将数据映射到某些POJO对象。

解决方法 - 在打开db时使用try / catch,如果发生异常,请在一段时间(1秒)后尝试执行此操作。