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 崩毁的原因。
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 来处理并发的情况
现在你可以线程安全地使用你的数据库连接了。
原文: