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

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

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

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

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

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

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

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

public class DatabaseManager {    private static DatabaseManager instance;    private static SQLiteOpenHelper mDatabaseHelper;    public static synchronized void initialize(Context context, SQLiteOpenHelper helper) {        if (instance == null) {            instance = new DatabaseManager();            mDatabaseHelper = helper;        }    }    public static synchronized DatabaseManager getInstance() {        if (instance == null) {            throw new IllegalStateException(DatabaseManager.class.getSimpleName() +                    " is not initialized, call initialize(..) method first.");        }        return instance;    }    public synchronized SQLiteDatabase getDatabase() {        return new mDatabaseHelper.getWritableDatabase();    }}

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

// In your application class DatabaseManager.initializeInstance(getApplicationContext()); // Thread 1 DatabaseManager manager = DatabaseManager.getInstance(); SQLiteDatabase database = manager.getDatabase() database.insert(…); database.close(); // Thread 2 DatabaseManager 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

示例:

public class DatabaseManager {    private AtomicInteger mOpenCounter = new AtomicInteger();    private static DatabaseManager instance;    private static SQLiteOpenHelper mDatabaseHelper;    private SQLiteDatabase mDatabase;    public static synchronized void initializeInstance(SQLiteOpenHelper helper) {        if (instance == null) {            instance = new DatabaseManager();            mDatabaseHelper = helper;        }    }    public static synchronized DatabaseManager getInstance() {        if (instance == null) {            throw new IllegalStateException(DatabaseManager.class.getSimpleName() +                    " is not initialized, call initializeInstance(..) method first.");        }        return instance;    }    public synchronized SQLiteDatabase openDatabase() {        if(mOpenCounter.incrementAndGet() == 1) {            // Opening new database            mDatabase = mDatabaseHelper.getWritableDatabase();        }        return mDatabase;    }    public synchronized void 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 本文由zhiweiofli编辑发布,转载请注明出处,谢谢。

更多相关文章

  1. Android系统配置数据库注释(settings.db)
  2. Android下Excel的操作
  3. 【Android】文件读写操作(含SDCard的读写)
  4. Android(安卓)中数据库查询方法 query() 中的 select
  5. [置顶] android orm映射框架(类似hibernate)基本使用
  6. android 百度地图3.0+常用操作
  7. 转:Android下文件操作模式(含SDCard的读写)
  8. android音频、视频、拍照基础操作
  9. Android之再谈文件操作和SDcard读写

随机推荐

  1. Kafka多维度系统精讲,从入门到熟练掌握
  2. 深入了解Serializable接口
  3. 表格和用户注册
  4. zabbix4.0监控PG数据库感受
  5. 高逼格企业级MySQL数据库备份方案,原来是
  6. 【每天一题】php面试时,你是否经常被问到?
  7. 窥探redis为何会变慢
  8. 【每天一题】PHP中常用的数组操作方法笔
  9. 这些PHP考点虽然简单基础,但是很重点
  10. PHP实现财务审核通过后返现金额到客户的