Sandy
Sandy

Reputation: 2690

SQLite exception: Database is locked issue

I'm having an issue with my SQLite database inside an Android app. It seems to happen every so often and I cannot reproduce it, but this is the report the Android Market gives.

Basically I have first a splash screen activity that starts by checking if all the needed data is in the database. If not, it will install the data in a asynchronous thread and close the db connection.

Then and only then can the main activity be started, which also opens and reads/writes the database.

This code is executed from the onCreate method of the splash screen activity:

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("");
}

The code for the asynchronous thread is:

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);
    }
}

These are important parts of the WordDBAdapter class, which is also used by the main activity:

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();
    }
    ...
}

I'm getting the following exceptions, which are similar but have a different message:

First type of error message:

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)

Second type of error message:

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)

I would really like to not have to create a ContentProvider, because I believe its overkill if a simpler solution is available. Also, only this application has access to the database.

Any suggestions on how this can be fixed?

Upvotes: 15

Views: 44530

Answers (7)

Oleksandr Abakumov
Oleksandr Abakumov

Reputation: 41

These lines of code resolved my (the same) problem:

(I had my transaction opened before...)

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

Upvotes: 0

sanath_p
sanath_p

Reputation: 2218

This was the Reason for me:

I forgot to end transaction in my function call after inserting into db .So when i tried to insert another record from another activity i got database locked exception. Adding the below statement solved my issue.

sqlDB.endTransaction();

Upvotes: 5

dbDev
dbDev

Reputation: 1549

Looks like this is an older question, but maybe this will help someone. I was having the same issue, I was loading probably around a dozen tables in one database and would regularly crash, not always, but sometimes.

Finally, and this may be hackey, I just created a separate database for each table. So now I have a dozen databases, each with one table. But, no more exceptions. Code was sounds, was creating, opening and closing correctly, just seemed to be getting collisions somewhere.

Like I said you may consider it hackey, but it fixed my issue.

db

Upvotes: 1

Shaun
Shaun

Reputation: 5531

If your using Fragments and it happens with orientation changes, its because you have two instances of the same Fragment and they both have their own instance of a Db helper class of some kind. In order to make it work you either have to destroy your fragment on orientation change and rebuild it, or find a way to destroy the Db helper class. This is only for Fragments with orientation issues. I had to do alot of troubleshooting to figure it out, so hopefully someone finds this helpful

Upvotes: 5

Mudassar
Mudassar

Reputation: 1576

After doing some effort I did it (My app was working on perfect till android 2.3 but got db lock error when I used to run it on HoneyComb tablet).
I did using Semaphores (using lock in critical sections).

Example 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();
        }
    }

}

Example 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();
        }
    }


}

Hope this would help Anyone in future :)

Upvotes: 19

Khawar
Khawar

Reputation: 5227

May be this is because you havn't properly opened or created database. give it a try if this is the case.

mDb = mCtx.openOrCreateDatabase(DatabaseName, SQLiteDatabase.CREATE_IF_NECESSARY, null);

in the DatabaseHelper Class:

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

Upvotes: 0

slkorolev
slkorolev

Reputation: 6001

I would suggest you to close your DB in the main thread and open and close it in your doInBackground method:

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

Also you should not directly call UI methods from AsyncTask as UI is not thread safe. Instead call method updateProgress from doInBackground and update your progressDialog from onProgressUpdate as onProgressUpdate is called from the main thread.

Upvotes: 1

Related Questions