Android上的SQLite数据库,多线程,锁定和帐户同步

我正在尝试获得一个对我的sqlite数据库的多线程访问没有失败的模式.另外,让我疯狂的是我无法重现这个问题.

我有一个使用数据库的应用程序,还有Android帐户和Android同步来同步我的应用程序的数据.我的猜测是,当两者同时发生时,它会崩溃.我遇到了很多错误:

* android.database.sqlite.SQLiteDatabaseLockedException: database is locked
 * android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
 * android.database.sqlite.SQLiteDatabaseLockedException: error code 5: database is locked
 * android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5):,while compiling: PRAGMA journal_mode
 * android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 778)
 * android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/net.bicou.redmine/databases/redmine.db' to 'en_US'. \n Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

也许并非所有这些都与相同的根本原因有关,但我有点迷失.

我有的是:

>一个抽象基类,DbAdapter,由想要管理单个表的子类扩展
>一个管理SQLite数据库的类,名为DbManager,包含一个Lock

现在,用户有一个不是单身的DbManager版本.我打算让DbManager成为单例,以便所有线程共享同一个对象.这不应该是一个问题,因为据我所知,后台同步和应用程序共享相同的过程.

这是课程(只有相关部分):

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 with singleton version
    }

    public synchronized DbAdapter open() throws SQLException {
        if (mDb != null) {
            return this;
        }

        mDbManager = new DbManager(mContext); // currently in production
        mDbManager = DbManager.instance(mContext); // currently investigating this singleton solution
        try {
            mDb = mDbManager.getWritableDatabase();
        } catch (final SQLException e) {
            L.e("Unable to 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();// This may crash
        }

        return this;
    }

    public synchronized void close() {
        mDbManager.close();
        mDbManager = null;
        mDb = null;
    }
}

需要处理数据库表的类将扩展DbAdapter,并实现select,insert,delete等方法.

这是数据库管理员:

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

    // Currently in prod
    public DbManager(final Context context) {
        super(context,DB_FILE,null,DB_VERSION);
        mContext = context;
    }

    // singleton version will make this constructor private and 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) {
        for (final String sql : statements) {
            try {
                db.execSQL(sql);
            } catch (final Exception e) {
                L.e("Unable to create table: " + sql,e);
            }
        }
    }
}

好的,现在,问题.

>我的锁是否正确实施?我真的很陌生,我不知道ReentrantLock是不是一个好选择,如果我在适当的时候锁定/解锁
>我的同步方法是否正确实施?我的意思是,我已将synchronized关键字放在我不希望被并发线程中断的方法周围.这是正确的吗?你可以就我的同步使用提出建议吗?
>我该如何重现这个问题?我创建了一个测试,它使用3个线程对数据库进行并发读/写访问,并使用一些Thread.sleep来确保每个线程的db打开/关闭重叠,但它不会崩溃.这真是烦我,我不认为有很多人有这个问题,所以我不知道如何重现.
>我的DbAdapter DbManager技术选择是个好主意吗?有更好的模式吗?
>让DbManager成为单身人士是个好主意吗?

解决方法

对于多线程访问,建议使用单例模式.

这样,对同一数据库的连续调用将被无缝序列化.

但是,在插入上有一些NullPointerExceptions并非不可能.因此,要扩展“Thread.sleep”逻辑,可以使用以下代码:

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

相关文章

Android 如何解决dialog弹出时无法捕捉Activity的back事件 在...
Android实现自定义带文字和图片的Button 在Android开发中经常...
Android 关于长按back键退出应用程序的实现最近在做一个Andr...
android自带的时间选择器只能精确到分,但是对于某些应用要求...