Question

I don't know how to handle this correctly without getting database locked errors.

My app basically downloads many items in batches of 100 rows (~ 60.000 items) and inserts them in the database. Each batch of 100 rows is processed into a transaction. The main activity allows the user to navigate between screens (fragments) while records are being downloaded and inserted. Most of the other screens contains read data from the database. I get a lot of database lock errors during reading. All readings are done in the main activity (not fragments) in different async tasks

So far I just used the "classic approach"

public class DBAdapter {
public DBAdapter(Context ctx) {
        this.context = ctx;
        DBHelper = new DatabaseHelper(context);
    }


    private static class DatabaseHelper extends SQLiteOpenHelper {


        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);

        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DB_CREATE_TABLES);

        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Utils.log("Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            onCreate(db);
        }

    public DBAdapter open() throws SQLException {
        database = DBHelper.getWritableDatabase();
        return this;
    }

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

Then on my activity's onCreate() I call db = new DBAdapter(context); and each time I am doing an database operation (read/write) I call db.open() and after the insert/read is done I call db.close()

My questions are:

  1. what would be the best approach to this situation ?
  2. Considering I do a lot of write/read would it be better to call db.open on onCreate and db.close() on onDestroy() ? Would this be better than calling open/close each time I need to access the database ?
  3. What do I need to do to avoid database locking on reading ?
Was it helpful?

Solution

I had a exactly similar situation like yours. In addition to what you described, in my app the user also can update the database through input on the screen. The way I resolved it ( I don't know if it's the best way, but I hardly see any locking issue now)

  • Make a singleton class derived from SQLiteOpenHelper to make sure only one instance is running at any given time.
  • Implement ContentProvider class for insert/update/delete/query operations. Make all those functions 'synchronized'
  • Only close the db in ContentProvider's shutdown function. I do a very frequent db operations, so I don't want to open/close everytime. But I am not sure if it's the correct way of handling it.
  • Do access DB only through ContentProvider interface from anywhere

OTHER TIPS

A very simple approach, or maybe a workaround is using synchronized methods for opening and closing the database object. I don't really know if it's the best practice, but at least it's simple and easy. Add this methods to your DBAdapter Class, and use them instead of db.open and db.close. The use_count attribute simple holds how many times open has been called. Initialize it with a value of 0. Also, in order to make it work on your solution be sure to pass the same DBAdapter object between the fragments. Don't create a new one everytime :

private int use_count = 0;
public synchronized void doOpen()
{
    use_count++;
    this.open();
}

public synchronized void doClose()
{
    use_count--;
    if (use_count == 0)
    {
        this.close();
    }
}

Consider wrapping the SQLite database in a ContentProvider and using CursorLoader to do the queries from the various activities & fragments. This isolates the management of the database from the Activity/Fragment life cycle and can result in many fewer open/close cycles.

You may still run into contention between the reads and writes, but having all the database interaction in the same module should make it easier for you to address these issues.

Some interesting links: http://www.vogella.com/articles/AndroidSQLite/article.html#todo

When to use a Content Provider

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top