SQLite database, multithreading, Locks and account sync on android -
i'm trying pattern doesn't fail multithreaded access sqlite database. also, driving me nuts can't reproduce issue.
i have app uses db, android accounts , android sync sync app's data. guess when 2 happen same time, crashes. i'm getting lot of errors like:
* android.database.sqlite.sqlitedatabaselockedexception: database locked * android.database.sqlite.sqlitedatabaselockedexception: database locked (code 5) * android.database.sqlite.sqlitedatabaselockedexception: error code 5: database locked * android.database.sqlite.sqlitedatabaselockedexception: database locked (code 5): , while compiling: pragma journal_mode * android.database.sqlite.sqlitediskioexception: disk i/o error (code 778) * android.database.sqlite.sqliteexception: failed change locale db '/data/data/net.bicou.redmine/databases/redmine.db' 'en_us'. \n caused by: android.database.sqlite.sqlitedatabaselockedexception: database locked (code 5) maybe not of them related same root cause, i'm kind of lost.
what have is:
- an abstract base class,
dbadapter, extended subclasses want manage single table - a class manages sqlite database, called
dbmanager, containslock
right users have version of dbmanager not singleton. i'm planning make dbmanager singleton, threads share same object. shouldn't problem, because far have understood/seen, background sync , app share same process.
here classes (only relevant parts):
public abstract class dbadapter { context mcontext; protected dbmanager mdbmanager; sqlitedatabase mdb; public static final string key_rowid = "_id"; public dbadapter(final context ctx) { mcontext = ctx; } public dbadapter(final dbadapter other) { mcontext = other.mcontext; mdb = other.mdb; mdbmanager = other.mdbmanager; // removed singleton version } public synchronized dbadapter open() throws sqlexception { if (mdb != null) { return this; } mdbmanager = new dbmanager(mcontext); // in production mdbmanager = dbmanager.instance(mcontext); // investigating singleton solution try { mdb = mdbmanager.getwritabledatabase(); } catch (final sqlexception e) { l.e("unable open db, trying again in 1 second", e); try { thread.sleep(1000); } catch (final interruptedexception e1) { l.e("could not wait 1 second " + e1); } mdb = mdbmanager.getwritabledatabase();// may crash } return this; } public synchronized void close() { mdbmanager.close(); mdbmanager = null; mdb = null; } } a class needs handle database table extend dbadapter, , implement methods such select, insert, delete, etc.
here's db manager:
public class dbmanager extends sqliteopenhelper { private static final string db_file = "db"; private static final int db_version = 15; context mcontext; lock mlock = new reentrantlock(); // in prod public dbmanager(final context context) { super(context, db_file, null, db_version); mcontext = context; } // singleton version make constructor private , add: private static dbmanager minstance; public static synchronized dbmanager instance(context context) { if (instance == null) { instance = new dbmanager(context); } return instance; } @override public sqlitedatabase getwritabledatabase() { mlock.lock(); return super.getwritabledatabase(); } @override public void close() { super.close(); mlock.unlock(); } @override public void oncreate(final sqlitedatabase db) { // ... } @override public void onupgrade(final sqlitedatabase db, final int oldversion, final int newversion) { // ... } private void createtables(final sqlitedatabase db, final string[] statements) { (final string sql : statements) { try { db.execsql(sql); } catch (final exception e) { l.e("unable create table: " + sql, e); } } } } ok, now, questions.
- is lock implemented? i'm new this, don't know if
reentrantlockchoice, , if i'm locking/unlocking @ right moment - are
synchronizedmethod implemented? mean, have placedsynchronizedkeyword around methods don't want interrupted concurrent threads. right? can advice onsynchronizeduse? - how can reproduce issue? have created test uses 3 threads make concurrent read/write access db, , use
thread.sleepensure db open/close each thread overlap, doesn't crash. bugging me, don't think there lot of people have issue, don't know how reproduce. - is
dbadapter+dbmanagertechnical choice idea? there better pattern? - is idea make
dbmanagersingleton?
for multiple threads accessing, advisable use singleton pattern.
such way, successive calls same database seamlessly serialised.
however, it's not impossible have nullpointerexceptions on inserts. so, expand "thread.sleep" logic, use code:
@override public sqlitedatabase getwritabledatabase() { while (true) { try { return super.getwritabledatabase(); } catch (sqlitedatabaselockedexception e) { system.err.println(e); } try { thread.sleep(500); } catch (interruptedexception e) { system.err.println(e); } } }
Comments
Post a Comment