Android多線程下安全訪問資料庫(Concurrent Database Access)

來源:互聯網
上載者:User

標籤:android   style   blog   http   io   ar   color   os   sp   

Assuming you have your own SQLiteOpenHelper:  

1 public class DatabaseHelper extends SQLiteOpenHelper { ... }

 

Now you want to write data to database in separate threads:

 1 // Thread 1 2  Context context = getApplicationContext(); 3  DatabaseHelper helper = new DatabaseHelper(context); 4  SQLiteDatabase database = helper.getWritableDatabase(); 5  database.insert(…); 6  database.close(); 7  8  // Thread 2 9  Context context = getApplicationContext();10  DatabaseHelper helper = new DatabaseHelper(context);11  SQLiteDatabase database = helper.getWritableDatabase();12  database.insert(…);13  database.close();

 

You will get following message in your logcat and one of your changes will not be written:

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

This is happening because every time you create new SQLiteOpenHelper object you are actually making new database connection. If you try to write to the database from actual distinct connections at the same time, one will fail.

To use database with multiple threads we need to make sure we are using one database connection.

Let’s make singleton class DatabaseManager which will hold and return single SQLiteOpenHelper object.

 1 public class DatabaseManager { 2  3     private static DatabaseManager instance; 4     private static SQLiteOpenHelper mDatabaseHelper; 5  6     public static synchronized void initializeInstance(SQLiteOpenHelper helper) { 7         if (instance == null) { 8             instance = new DatabaseManager(); 9             mDatabaseHelper = helper;10         }11     }12 13     public static synchronized DatabaseManager getInstance() {14         if (instance == null) {15             throw new IllegalStateException(DatabaseManager.class.getSimpleName() +16                     " is not initialized, call initialize(..) method first.");17         }18 19         return instance;20     }21 22     public synchronized SQLiteDatabase getDatabase() {23         return mDatabaseHelper.getWritableDatabase();24     }25 26 }
Updated code which write data to database in separate threads will look like this.
 1  // In your application class 2  DatabaseManager.initializeInstance(new DatabaseHelper()); 3  4  // Thread 1 5  DatabaseManager manager = DatabaseManager.getInstance(); 6  SQLiteDatabase database = manager.getDatabase() 7  database.insert(…); 8  database.close(); 9 10  // Thread 211  DatabaseManager manager = DatabaseManager.getInstance();12  SQLiteDatabase database = manager.getDatabase()13  database.insert(…);14  database.close();

This will bring you another crash:

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

Since we are using only one database connection, method getDatabase() return same instance of SQLiteDatabaseobject for Thread1 and Thread2. What is happening, Thread1 may close database, while Thread2 is still using it. That’s why we have IllegalStateException crash.


We need to make sure no-one is using database and only then close it. Some folks on stackoveflow recommended to never close your SQLiteDatabase. This will honor you with following logcat message. So I don‘t think this is good idea at all.


Leak foundCaused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed

Working sample:
 1 public class DatabaseManager { 2  3     private AtomicInteger mOpenCounter = new AtomicInteger(); 4  5     private static DatabaseManager instance; 6     private static SQLiteOpenHelper mDatabaseHelper; 7     private SQLiteDatabase mDatabase; 8  9     public static synchronized void initializeInstance(SQLiteOpenHelper helper) {10         if (instance == null) {11             instance = new DatabaseManager();12             mDatabaseHelper = helper;13         }14     }15 16     public static synchronized DatabaseManager getInstance() {17         if (instance == null) {18             throw new IllegalStateException(DatabaseManager.class.getSimpleName() +19                     " is not initialized, call initializeInstance(..) method first.");20         }21 22         return instance;23     }24 25     public synchronized SQLiteDatabase openDatabase() {26         if(mOpenCounter.incrementAndGet() == 1) {27             // Opening new database28             mDatabase = mDatabaseHelper.getWritableDatabase();29         }30         return mDatabase;31     }32 33     public synchronized void closeDatabase() {34         if(mOpenCounter.decrementAndGet() == 0) {35             // Closing database36             mDatabase.close();37 38         }39     }40 }

And use it as follows.

1 SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();2 database.insert(...);3 // database.close(); Don‘t close it directly!4 DatabaseManager.getInstance().closeDatabase(); // correct way

Every time you need database you should call openDatabase() method of DatabaseManager class. Inside this method, we have a counter, which indicate how many times database is opened. If it equals to one, it means we need to create new database, if not, database is already created.

The same happens in closeDatabase() method. Every time we call this method, counter is decreased, whenever it goes to zero, we are closing database.

Now you should be able to use your database and be sure - it‘s thread safe.

 

from:github

 

Android多線程下安全訪問資料庫(Concurrent Database Access)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.