Android SQLite中的并发问题

时间:2012-04-26 11:14:48

标签: android sqlite concurrency locking android-asynctask

我在运行AsyncTask时遇到问题,该AsyncTask对本地SQLite数据库执行长删除操作。如果用户旋转屏幕,则会重新创建Activity,并在AsyncTask运行时创建与数据库的新连接。

我知道SQLite一次支持多次阅读和一次写入,情况就是这样,所以我不明白为什么在删除时打开数据库时可以崩溃的原因......

失败是下一个:

04-26 12:54:21.839: I/Database(3925): sqlite returned: error code = 5, msg = database is locked
04-26 12:54:21.839: E/Database(3925): SELECT locale FROM android_metadata failed
04-26 12:54:21.839: E/Database(3925): Failed to setLocale() when constructing, closing the database
04-26 12:54:21.839: E/Database(3925): android.database.sqlite.SQLiteException: database is locked
04-26 12:54:21.839: E/Database(3925):   at android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
04-26 12:54:21.839: E/Database(3925):   at android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1967)
04-26 12:54:21.839: E/Database(3925):   at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1835)
04-26 12:54:21.839: E/Database(3925):   at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)
04-26 12:54:21.839: E/Database(3925):   at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:197)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:172)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.openReadableDatabaseWithForeignKeySupport(DatabaseManager.java:180)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.open(DatabaseManager.java:157)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.persistence.DatabaseManager.<init>(DatabaseManager.java:147)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.openerp.models.ConnectionProfile$ConnectionProfileManager.<init>(ConnectionProfile.java:138)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.activities.SelectConnectionProfile.fillData(SelectConnectionProfile.java:71)
04-26 12:54:21.839: E/Database(3925):   at com.caumons.trainingdininghall.activities.SelectConnectionProfile.onResume(SelectConnectionProfile.java:66)
04-26 12:54:21.839: E/Database(3925):   at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1149)
04-26 12:54:21.839: E/Database(3925):   at android.app.Activity.performResume(Activity.java:3833)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2085)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2110)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1643)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2796)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread.access$1600(ActivityThread.java:117)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:932)
04-26 12:54:21.839: E/Database(3925):   at android.os.Handler.dispatchMessage(Handler.java:99)
04-26 12:54:21.839: E/Database(3925):   at android.os.Looper.loop(Looper.java:123)
04-26 12:54:21.839: E/Database(3925):   at android.app.ActivityThread.main(ActivityThread.java:3647)
04-26 12:54:21.839: E/Database(3925):   at java.lang.reflect.Method.invokeNative(Native Method)
04-26 12:54:21.839: E/Database(3925):   at java.lang.reflect.Method.invoke(Method.java:507)
04-26 12:54:21.839: E/Database(3925):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
04-26 12:54:21.839: E/Database(3925):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
04-26 12:54:21.839: E/Database(3925):   at dalvik.system.NativeStart.main(Native Method)

我编写了以下解决方法,但我认为这不是很有礼貌:

private void openReadableDatabaseWithForeignKeySupport() {
    try {
        mDatabase = mDatabaseHelper.getReadableDatabase();
        mDatabase.execSQL("PRAGMA foreign_keys = ON");
    } catch (SQLiteException e) {
        /*
         * If there is an exception because the database is locked,
         * we will retry until it is free and can be opened.
         * */
        Log.d(getClass().getName(), e.toString());
        openReadableDatabaseWithForeignKeySupport(); // Recursive call
    }
}

最令人好奇的是,似乎失败的是Android自动调用的setLocale()方法,但docs表示:

  

setLocale()设置此数据库的区域设置。如果此数据库设置了NO_LOCALIZED_COLLATORS标志或以只读方式打开,则不执行任何操作。

任何帮助将不胜感激! :)

1 个答案:

答案 0 :(得分:2)

像这样定期打开和关闭数据库并不是一个好主意。在应用程序启动时打开一次,并在整个应用程序的生命周期内重用此连接。你甚至不需要打扰它。 SQLite自己管理并发,因此您也不必担心多个线程。

这将使您的所有“数据库锁定”错误消失,并使您的代码更整洁,更简单。