当前位置:  开发笔记 > 数据库 > 正文

Android线程和数据库锁定

如何解决《Android线程和数据库锁定》经验,为你挑选了4个好方法。

我们AsyncTasks用来访问数据库表和游标.

不幸的是,我们偶尔会看到有关数据库被锁定的例外情况.

E/SQLiteOpenHelper(15963): Couldn't open iviewnews.db for writing (will try read-only):
E/SQLiteOpenHelper(15963): android.database.sqlite.SQLiteException: database is locked
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1637)
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.(SQLiteDatabase.java:1587)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:638)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:659)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:652)
E/SQLiteOpenHelper(15963):  at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:482)
E/SQLiteOpenHelper(15963):  at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158)
E/SQLiteOpenHelper(15963):  at com.iview.android.widget.IViewNewsTopStoryWidget.initData(IViewNewsTopStoryWidget.java:73)
E/SQLiteOpenHelper(15963):  at com.iview.android.widget.IViewNewsTopStoryWidget.updateNewsWidgets(IViewNewsTopStoryWidget.java:121)
E/SQLiteOpenHelper(15963):  at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:338)
E/SQLiteOpenHelper(15963):  at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:1)
E/SQLiteOpenHelper(15963):  at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:256)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.FutureTask.run(FutureTask.java:122)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:648)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:673)
E/SQLiteOpenHelper(15963):  at java.lang.Thread.run(Thread.java:1060)

有没有人有一个代码的一般例子,它从一个读取的不同线程写入数据库,我们如何确保线程安全.

我有一个建议是使用a ContentProvider,因为这将处理来自多个线程的数据库访问.我将看看这个,但这是处理这样一个问题的推荐方法吗?考虑到我们在前面或后面谈论,它似乎相当重量级.



1> Pandalover..:

我们最后用了一个ContentProvider.这似乎可以解决问题.


ContentProvider解决问题的原因是因为它通常使用一个单独的`SQLiteOpenHelper`,这意味着只有一个连接到数据库,而底层的`SQLiteDatabase`负责锁定.您不需要ContentProvider - 只需确保不使用2个不同的数据库连接写入数据库.本文介绍了锁定在Android上的工作原理.http://kagii.squarespace.com/journal/2010/9/10/android-sqlite-locking.html
更新了文章的链接:http://kagii.com/post/6828016869/android-sqlite-locking
来自第二个链接的源代码已死:/
所有链接都死了!

2> ubzack..:

我只是通过确保所有数据库打开都已关闭来解决同样的异常,并且(更重要的是)确保这一点,使每个数据库实例的范围仅限于需要它的方法.ContentProvider是从多个线程访问数据库时使用的一个好的,安全的类,但也要确保使用良好的数据库实践:

将db实例保持为本地(没有SQLiteDatabase类成员!)

close()以与打开它相同的方法调用db

调用close()你从db获得的游标

听取LogCat对SQLiteDatabse可能有的任何抱怨


经过一些阅读后,我认为不是每个请求打开自己的数据库实例的推荐方式.AFAIK如果2个线程使用2个差异db实例同时写入数据库,则只有一个将写入,另一个则忽略而不会出现错误(只是日志消息).数据库锁是乐观主义者,您可以打开尽可能多的连接,但是您应该只使用一个数据库实例进行写入.如我错了请纠正我

3> Italo Borssa..:

在一些代码之前,让我们恢复一些方法:

信号量:目前为止提供的最佳解决方案.它是问题的核心:资源共享!它将处理数据库访问的锁定,避免冲突(database is locked).

Java同步:一种信号量实现,但不那么软化.使用synchronized您将无法轻松解决涉及交易的一些案例.

ContentProvider:实现ContentProvider仅针对某些情况解决问题(或扫除地毯下的问题).你还会遇到同样的问题.区别在于ContentProvider模式将指导您在访问Sqlite数据库时不会出现一些常见错误.该 ContentProvider的文档说:"你不需要供应商如果使用完全是你自己的应用程序中使用SQLite数据库."

几乎是强制性的:保持db实例本地,close()使用finally语句打开它的同一方法调用db,使用语句close()使用游标finally等等几乎是必须的,以避免使用Sqlite的问题.

让我们展示一个Moss提供的信号量解决方案的例子,我从CL中获取.并且适合交易.

class DataAccess {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data readSomething(int id) {
        Cursor c = null;
        r.lock();
        try {
            c = getReadableDatabase().query(...);
            return c.getString(0);
        } finally {
            if (c != null) c.close();
            r.unlock();
        }
    }

    public void changeSomething(int id, int value) {
        w.lock();
        try {
            getWritableDatabase().update(...);
        } finally {
            w.unlock();
        }
    }

    private void beginTransactionWithSemaphores() {
        getWritableDatabase().beginTransactionWithListener(new SQLiteTransactionListener() {
            @Override
            public void onBegin() {
                w.lock();
            }

            @Override
            public void onRollback() {
                w.unlock();
            }

            @Override
            public void onCommit() {
                w.unlock();
            }
        });
    }
}



4> Moss..:

考虑到SQLite数据库是基于文件的,并不打算以多进程方式访问.将SQLite与多处理混合的最佳过程是在每个与数据库相关的访问中使用信号量(aquire(),release()).

如果您创建一个获取/释放全局信号量的Db包装器,那么您的数据库访问将是线程安全的.实际上这意味着您可以获得一个bootleneck,因为您正在排队访问数据库.因此,如果它是一个改变数据库的操作,你只能用信号量包装访问,所以当你改变数据库时,没有人能够访问它并等到写过程完成.

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有