在多线程应用程序Android中访问数据库的最佳方法? [英] Best way to access database in multi threaded application Android?

查看:87
本文介绍了在多线程应用程序Android中访问数据库的最佳方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:请不要将此问题标记为重复.我经历了几个类似的问题,但找不到满意的答案.

我一直在研究使用Sqlite数据库的应用程序.我们遵循单例模式,以确保我们在整个应用程序中只能创建一个辅助类的实例.

I have been working on an application which uses Sqlite Database. We are following singleton pattern which ensures that we can create only one instance of our helper class throughout our application.

public class CustomSqliteHelper extends SQLiteOpenHelper {

  public static CustomSqliteHelper getInstance(Context context) {
    if (instance == null) {
      synchronized (CustomSqliteHelper.class) {
        if (instance == null) {
          instance = new CustomSqliteHelper(context);
        }
      }
    }
    return instance;
  }
}

但是有时应用程序会因SQLiteDatabaseLockedException而崩溃.我理解,当多个线程/进程试图一次写入数据库时​​,会出现此异常.即使在写操作仍在进行时,一个线程/进程尝试读取数据库,也会抛出此异常.

But sometimes the application crashes with SQLiteDatabaseLockedException. I understand this exception comes when more than one thread/process tries to write to the database at one time. Even if one thread/process tries to read the database when the write operation is still going on, this exception will be thrown.

因此,我已经阅读了很多有关这种情况以及防止这种情况发生的可能方法.许多文章建议使用ContentProvider而不是直接扩展SqliteOpenHelper类并在数据库对象上执行操作.在阅读其中一则帖子时,此帖子提到:使用Content Provider时,无需手动维护多线程环境.

So I have been reading a lot about this and the possible ways to prevent this from happening. A lot of posts suggests using ContentProvider instead of directly extending SqliteOpenHelper class and performing operations on the database object. While reading one of the posts, this post mentioned that while using Content Provider, you don’t need to manually take care of the multi threaded environment.

尽管ContentProvider缺乏线程安全性,但通常情况下,您会发现不需要采取任何进一步的措施来防止潜在的竞争状况.典范的例子是当ContentProvider由SQLiteDatabase支持时.当两个线程尝试同时写入数据库时​​,SQLiteDatabase将锁定自身,以确保一个线程将等到另一个线程完成为止.每个线程将被授予对数据源的互斥访问权限,以确保满足线程安全性.

Although the ContentProvider lacks in thread-safety, often times you will find that no further action is required on your part with respect to preventing potential race conditions. The canonical example is when your ContentProvider is backed by a SQLiteDatabase; when two threads attempt to write to the database at the same time, the SQLiteDatabase will lock itself down, ensuring that one will wait until the other has completed. Each thread will be given mutually exclusive access to the data source, ensuring the thread safety is met.

上面的引用似乎令人困惑,因为首先它提到ContentProvider不支持线程安全.但他得出的结论是,应用程序开发人员无需为实现并发而做任何事情.

The above quote seems sounds confusing because first it mentions that ContentProvider does not support thread safety. But he concludes that the application developer doesn't need to do anything on his part to achieve concurrency.

此外,如果我选择使用SqliteOpenHelper,那么防止这些崩溃的最佳方法是什么?我一直在考虑对每个数据库操作使用锁.

Also, if I choose to use SqliteOpenHelper, what will be the best way to prevent these crashes? I have been thinking of using locks for every db operation.

 public class CustomSqliteHelper extends SQLiteOpenHelper {

 private String lock = "lock";

 public void insert(){
    synchronized(lock){
       // Do the insert operation here.
    }
 }


 public void update(){
    synchronized(lock){
       // Do the update operation here.
    }
 }
 }

但是我的一个团队成员建议我不要这样做,因为Java锁很昂贵.

But one of my team members advised me not to do so because Java locks are expensive.

经过最流行的项目 Github,我发现开发人员建议将每个数据库操作包装在事务中.

After going through one of the most popular projects on Github, I found that the developers have advised to wrap every database operation inside a transaction.

public void insert(ContentValues values) {
    // Create and/or open the database for writing
    SQLiteDatabase db = getWritableDatabase();

    // It's a good idea to wrap our insert in a transaction. This helps with performance and ensures
    // consistency of the database.
    db.beginTransaction();
    try {
        // The user might already exist in the database (i.e. the same user created multiple posts).

        db.insertOrThrow(TABLE_POSTS, null, values);
        db.setTransactionSuccessful();
    } catch (Exception e) {
        Log.d(TAG, "Error while trying to add post to database");
    } finally {
        db.endTransaction();
    }
}

我真的不确定这是否可以防止Lock异常?这似乎更像是一个面向性能的步骤.

I am really not sure if this can prevent the Lock exception? This seems more like a performance oriented step.

因此,最终在阅读了所有这些博客和教程之后,我仍然感到困惑.我的主要问题是

So finally after reading all those blogs and tutorials, I am still confused. My main questions are

  1. 哪个是使用ContentProviders或扩展的更好选择 SqliteOpenHelper假定我的应用程序不与之共享数据 其他应用程序.
  2. 正在将Java锁放在所有操作上 最佳方法还是有其他方法比这更好?
  1. Which is a better choice to use ContentProviders or extending SqliteOpenHelper given that my application doesn't share data with other applications.
  2. Is putting Java locks on all the operations the best approach or is there any other approach which is better than this?

更新:根据@Mustanar的回答,似乎SQLiteDatabase负责锁定机制.这意味着,如果您要执行写操作,则数据库将被锁定.但是同时,如果其他某个线程尝试执行写操作,那么第二个操作将处于等待状态,直到锁被释放,否则它将抛出android.sqlite.database.SQLiteDatabaseLockedException异常?

Update: Based on the answer by @Mustanar, it seems that the SQLiteDatabase takes care of the locking mechanism. It means that if you are doing a write opertaion, the database will be locked. But at the very same time, if some other thread tries to perform a write operation, then will the second operation be in waiting till the lock is released or will it throw an android.sqlite.database.SQLiteDatabaseLockedException exception ?

更新2 :悬赏这个问题,因为答案似乎仍然不清楚.我仅使用Helper类的一个实例.但是仍然出现此错误.

Update 2: Putting a bounty on this question, because the answer still seems to be not clear to me. I am using only one instance of the Helper class. But still getting this error.

PS:感谢您提出这么长的问题.

推荐答案

使用Singleton模式实例化SQLiteOpenHelper,因此在整个应用程序中应存在一个单例实例. 这将确保不会发生泄漏,并使您的工作变得更加轻松,因为它消除了在编写代码时忘记关闭数据库的可能性.它还将确保在整个应用程序中对数据库的安全访问.

Use a Singleton pattern to instantiate the SQLiteOpenHelper, so throughout the application one instance of singleton should exist. This will ensure that no leaks occur, and will make your life a lot easier since it eliminates the possibility of forgetting to close your database as you code. It also will ensure safe access to the database throughout the application.

此外,您不必实现自己的锁定机制. SQLiteDatabase保持锁定机制.因此,在特定时间只有一个事务在进行,而其他所有事务将使用Sigleton.getInstance()方法在Queue中.确保对数据库的单个访问点.

Moreover, You don't have to implement your own locking mecanism. SQLiteDatabase maintain the locking mecanism. So, There will only one transaction going at a particular time, all other transactions will be in Queue using the Sigleton.getInstance() approach. Ensure a single access point to the database.

此外,采用这种方法, 您不必关闭数据库连接,因为一旦完成交易,SQLiteDatabase就会释放所有引用. 因此,应该在您要执行CRUD操作并删除锁定的mecansim时使用.

Moreover, in this approach, You don't have to close your database connections, as SQLiteDatabase will be releasing all references once transaction has been done. So CustomSQLiteHelper.getInstance() should be used whenever you want to perform CRUD operations and remove your locking mecansim.

更多信息,请浏览博客 http: //www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html 并查看注释.

More information, Please go through the blog http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html and see the comments also.

希望这会有所帮助.

这篇关于在多线程应用程序Android中访问数据库的最佳方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆