【翻译】Android多线程下安全访问数据库


为了记录如何线程安全地访问你的Android数据库实例,我写下了这篇小小札记。文章中引用的项目代码请点击这里 假设你已编写了自己的SQLiteOpenHelper
publicclassDatabaseHelperextendsSQLiteOpenHelper{...}

现在你想在不同的线程中对数据库进行写数据操作:

// Thread 1Context context = getApplicationContext();DatabaseHelper helper =newDatabaseHelper(context);SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();// Thread 2Context context = getApplicationContext();DatabaseHelper helper =newDatabaseHelper(context);SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

然后在你的Logcat中将输出类似下面的日志信息,而你的写数据操作将会无效。

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

上面问题的出现,源于你每创建一个SQLiteOpenHelper 对象时,实际上也是在新建一个数据库连接。如果你尝试通过多个连接同时对数据库进行写数据操作,其一定会失败。

为确保我们能在多线程中安全地操作数据库,我们需要保证只有一个数据库连接被占用。

我们先编写一个负责管理单个SQLiteOpenHelper对象的单例DatabaseManager。

publicclassDatabaseManager{privatestaticDatabaseManager instance;privatestaticSQLiteOpenHelper mDatabaseHelper;publicstaticsynchronizedvoid initialize(Context context,SQLiteOpenHelper helper){if(instance ==null){
            instance =newDatabaseManager();
            mDatabaseHelper = helper;}}publicstaticsynchronizedDatabaseManager getInstance(){if(instance ==null){thrownewIllegalStateException(DatabaseManager.class.getSimpleName()+" is not initialized, call initialize(..) method first.");}return instance;}publicsynchronizedSQLiteDatabase getDatabase(){returnnew mDatabaseHelper.getWritableDatabase();}}

为了能在多线程中进行写数据操作,我们得修改一下代码,具体如下:

// In your application classDatabaseManager.initializeInstance(getApplicationContext());// Thread 1DatabaseManager manager =DatabaseManager.getInstance();SQLiteDatabase database = manager.getDatabase()
 database.insert(…);
 database.close();// Thread 2DatabaseManager manager =DatabaseManager.getInstance();SQLiteDatabase database = manager.getDatabase()
 database.insert(…);
 database.close();

然后又导致另个崩毁

java.lang.IllegalStateException: attempt to re-open an already-closed object:SQLiteDatabase

既然我们只有一个数据库连接,Thread1 和Thread2对方法getDatabase()的调用就会取得一样的SQLiteDatabase对象实例。之后的事情就是,当Thread1尝试管理数据库连接时,Thread2却仍然在使用该数据库连接。这也就是导致IllegalStateException崩毁的原因。

因此我们只能在确保数据库没有再被占用的情况下,才去关闭它。在stackoveflow上有一些讨论推荐“永不关闭”你的SQLiteDatabase 。 如果你这样做,你的logcat将会出现以下的信息,因此我不认为这是一个好主意。
Leak foundCaused by: java.lang.IllegalStateException:SQLiteDatabase created and never closed

示例:

publicclassDatabaseManager{privateAtomicInteger mOpenCounter =newAtomicInteger();privatestaticDatabaseManager instance;privatestaticSQLiteOpenHelper mDatabaseHelper;privateSQLiteDatabase mDatabase;publicstaticsynchronizedvoid initializeInstance(SQLiteOpenHelper helper){if(instance ==null){
            instance =newDatabaseManager();
            mDatabaseHelper = helper;}}publicstaticsynchronizedDatabaseManager getInstance(){if(instance ==null){thrownewIllegalStateException(DatabaseManager.class.getSimpleName()+" is not initialized, call initializeInstance(..) method first.");}return instance;}publicsynchronizedSQLiteDatabase openDatabase(){if(mOpenCounter.incrementAndGet()==1){// Opening new database
            mDatabase = mDatabaseHelper.getWritableDatabase();}return mDatabase;}publicsynchronizedvoid closeDatabase(){if(mOpenCounter.decrementAndGet()==0){// Closing database
            mDatabase.close();}}}

然后你可以怎样子去调用它:

SQLiteDatabase database =DatabaseManager.getInstance().openDatabase();
database.insert(...);// database.close(); Don't close it directly!DatabaseManager.getInstance().closeDatabase();// correct way

以后每当你需要使用数据库连接,你可以通过调用类 DatabaseManager 的方法openDatabase()。在方法里面,内置一个标志数据库被打开多少次的计数器。如果计数为1,代表我们需要打开一个新的数据库连接,否则,数据库连接已经存在。

在方法 closeDatabase() 中,情况也一样。每次我们调用 closeDatabase() 方法,计数器都会递减,直到计数为0,我们就需要关闭数据库连接了。

提示: 你应该使用 AtomicInteger 来处理并发的情况

现在你可以线程安全地使用你的数据库连接了。

原文:https://github.com/dmytrodanylyk/dmytrodanylyk/blob/gh-pages/articles/Concurrent%20Database%20Access.md

优质内容筛选与推荐>>
1、SpringBoot整合jsp
2、js 获取字符串中的中文
3、最新整理知识结构图
4、asp.net 2.0 用户管理功能结构
5、spring cloud


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号