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