SQLite异常:数据库已锁定问题

时间:2011-10-05 05:54:48

标签: android sqlite exception locking

我在Android应用中遇到了SQLite数据库的问题。它似乎经常发生,我无法重现它,但这是Android Market提供的报告。

基本上我首先有一个启动画面活动,首先检查所有需要的数据是否在数据库中。如果没有,它将在异步线程中安装数据并关闭数据库连接。

然后,只有这样才能启动主要活动,这也会打开并读取/写入数据库。

此代码从启动画面活动的onCreate方法执行:

dbWord = new WordDBAdapter(this, myUI);
dbWord.open();
if (dbWord.isDataInstalled()) {
    dbWord.close();
    databaseInstalled = true;
    clickToStartView.setText(myUI.PRESS_TO_START);
    clickToStartView.startAnimation(myBlinkAnim);
} else {
    // If not, try to install in asynchronous thread
    progressDialog = new ProgressDialog(this);
    progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    progressDialog.setMessage(myUI.INSTALLING_DB);
    progressDialog.setCancelable(false);
    new InstallDBData().execute("");
}

异步线程的代码是:

private class InstallDBData extends AsyncTask<String, Integer, Integer> {

    @Override
    protected Integer doInBackground(String... arg0) {
        progressDialog.setProgress(0);
        dbWord.installData(0, getApplicationContext());
        progressDialog.setProgress(20);
        dbWord.installData(1, getApplicationContext());
        progressDialog.setProgress(40);
        dbWord.installData(2, getApplicationContext());
        progressDialog.setProgress(60);
        dbWord.installData(3, getApplicationContext());
        progressDialog.setProgress(80);
        dbWord.installData(4, getApplicationContext());
        progressDialog.setProgress(100);
        return 1;
    }

    protected void onPreExecute() {
        progressDialog.show();
    }

    protected void onPostExecute(Integer x) {
        dbWord.close();
        progressDialog.hide();
        databaseInstalled = true;
        clickToStartView.setText(myUI.PRESS_TO_START);
        clickToStartView.startAnimation(myBlinkAnim);
    }
}

这些是WordDBAdapter类的重要部分,主要活动也使用它:

public class WordDBAdapter {
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    public WordDBAdapter open() throws android.database.SQLException {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        mDbHelper.close();
    }
    ...
}

我收到以下异常,这些异常相似但有不同的消息:

第一种错误信息:

java.lang.RuntimeException: Unable to start activity 
  ComponentInfo{example.flashcards.medical/com.example.flashcards.common.SplashWindow}: 
  android.database.sqlite.SQLiteException: database is locked: BEGIN EXCLUSIVE;
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1830)
....
Caused by: android.database.sqlite.SQLiteException: database is locked: BEGIN EXCLUSIVE;
at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1870)
at android.database.sqlite.SQLiteDatabase.beginTransactionWithListener(SQLiteDatabase.java:602)

第二类错误消息:

java.lang.RuntimeException: Unable to start activity 
  ComponentInfo{example.flashcards.medical/com.example.flashcards.common.SplashWindow}: 
  android.database.sqlite.SQLiteException: database is locked
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1768)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1784)
....
Caused by: android.database.sqlite.SQLiteException: database is locked
at android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
at android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1987)
at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1855)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)
at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:854)

我真的不想创建一个ContentProvider,因为我相信如果有更简单的解决方案,那就太过分了。此外,只有此应用程序才能访问数据库。

有关如何解决此问题的任何建议?

7 个答案:

答案 0 :(得分:16)

做了一些努力之后我做到了(我的应用程序正在完善直到android 2.3但是当我以前在HoneyComb平板电脑上运行它时会出现数据库锁错误。)
我确实使用了信号量(在关键部分使用锁定)。

示例1

public class DbExp extends SQLiteOpenHelper{
    public static String Lock = "dblock";
    private static final String DATABASE_NAME = "db.db";
    private static final String TABLE_NAME = "table_name";

    public void delete(Context mContext){
        synchronized(Lock) {
            SQLiteDatabase db  = getWritableDatabase();
            db.delete(TABLE_NAME, null, null);
            db.close();
        }
    }

    public void insert(){
        synchronized(Lock) {
            SQLiteDatabase db  = getWritableDatabase();
            db.insert(TABLE_NAME, ..., ...);
            db.close();
        }
    }

}

示例2

public class DB {
    public static String Lock = "dblock";
    private static final String DATABASE_NAME = "db.db";
    private static final String TABLE_NAME = "table_name";

    public void delete(Context mContext){
        synchronized(Lock) {
            SQLiteDatabase db  = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
            db.delete(TABLE_NAME, null, null);
            db.close();
        }
    }

    public void insert(Context mContext){
        synchronized(Lock) {
            SQLiteDatabase db  = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
            db.insert(TABLE_NAME, ..., ...);
            db.close();
        }
    }


}

希望这有助于将来的任何人:)

答案 1 :(得分:5)

如果您使用Fragments并且它发生了方向更改,那么因为您有两个相同Fragment的实例,并且它们都有自己的某种Db帮助程序类的实例。为了使它工作,你要么必须在方向改变时销毁你的片段并重建它,要么找到一种方法来销毁Db助手类。这仅适用于具有方向问题的片段。我不得不做很多故障排除才能搞清楚,所以希望有人发现这个有用的

答案 2 :(得分:4)

这就是我的理由:

我忘记在插入db。之后在我的函数调用中结束事务。当我试图从另一个活动插入另一个记录时,我得到了数据库锁定异常。添加以下语句解决了我的问题。

sqlDB.endTransaction();

答案 3 :(得分:1)

我建议您在主线程中关闭数据库,然后在doInBackground方法中打开并关闭它:

    @Override
    protected Integer doInBackground(String... arg0)
    {
        db.open();
        try {
        // your code here
        } finally {
            db.close()
        }            
        return 1;
    }

此外,您不应该直接从AsyncTask调用UI方法,因为UI不是线程安全的。而是从doInBackground调用方法updateProgress并从onProgressUpdate更新progressDialog,因为onProgressUpdate是从主线程调用的。

答案 4 :(得分:1)

看起来这是一个较旧的问题,但也许这会对某人有所帮助。我遇到了同样的问题,我在一个数据库中加载了大约十几个表,并且经常会崩溃,但并非总是如此,但有时会崩溃。

最后,这可能是hackey,我刚刚为每个表创建了一个单独的数据库。所以现在我有十几个数据库,每个数据库都有一个表。但是,没有例外。代码是声音,正在创建,正确打开和关闭,似乎只是在某处发生碰撞。

就像我说过你可能会认为它是hackey,但它解决了我的问题。

分贝

答案 5 :(得分:0)

可能是因为您没有正确打开或创建数据库。如果是这样的话,试一试。

mDb = mCtx.openOrCreateDatabase(DatabaseName, SQLiteDatabase.CREATE_IF_NECESSARY, null);
DatabaseHelper类中的

public DatabaseHelper(Context aContext)
{       
    mDb = aContext.openOrCreateDatabase(DatabaseName, SQLiteDatabase.CREATE_IF_NECESSARY, null);    
    OpenHelper openHelper = new OpenHelper(aContext, mDb );     
    mDb = openHelper.getWritableDatabase();
}

答案 6 :(得分:0)

这些代码行解决了我(相同)的问题:

(我之前打开了我的交易......)

writableDatabase.endTransaction();
writableDatabase.close();
dbHelper.close();