Eşzamanlı Veritabanı Erişimi
Blogumdaki aynı makale (daha fazla biçimlendirmeyi seviyorum)
Ben android veritabanı iş parçacığına güvenli erişim nasıl açıklayan küçük bir makale yazdı.
Kendi SQLiteOpenHelper'ınız olduğunu varsayarsak .
public class DatabaseHelper extends SQLiteOpenHelper { ... }
Şimdi veriyi ayrı bir iş parçacığında veritabanına yazmak istiyorsunuz.
// 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'inizde aşağıdaki mesajı alacaksınız ve değişikliklerinizden biri yazılmayacak.
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
Bu, yeni SQLiteOpenHelper nesnesi her oluşturduğunuzda aslında yeni veritabanı bağlantısı oluşturduğunuz için oluyor. Veritabanına aynı anda gerçek farklı bağlantılardan yazmaya çalışırsanız, biri başarısız olur. (yukarıdaki yanıttan)
Birden çok iş parçacıklı veritabanı kullanmak için tek bir veritabanı bağlantısı kullandığımızdan emin olmamız gerekir.
Tek SQLiteOpenHelper nesnesini tutacak ve döndürecek singleton sınıfı Veritabanı Yöneticisi yapalım .
public class DatabaseManager {
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
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 initialize(..) method first.");
}
return instance;
}
public SQLiteDatabase getDatabase() {
return new mDatabaseHelper.getWritableDatabase();
}
}
Veriyi veritabanına ayrı dizilerde yazan güncellenmiş kod şöyle görünecektir.
// In your application class
DatabaseManager.initializeInstance(new MySQLiteOpenHelper());
// 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();
Bu size başka bir kilitlenme getirecektir.
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
Yalnızca bir veritabanı bağlantısı kullandığımız için, getDatabase () yöntemi , Thread1 ve Thread2 için aynı SQLiteDatabase nesnesinin örneğini döndürür . Ne oluyor, Thread1 veritabanını kapatabilirken Thread2 hala onu kullanıyor. Bu yüzden IllegalStateException çökmesi var.
Kimsenin veritabanını kullanmadığından emin olmamız ve ancak sonra kapatmamız gerekir. Stackoveflow'daki bazı kişilerin SQLiteDatabase'inizi asla kapatmamaları önerilir . Bu, aşağıdaki logcat mesajıyla sonuçlanacaktır.
Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed
Çalışma örnek
public class DatabaseManager {
private int mOpenCounter;
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() {
mOpenCounter++;
if(mOpenCounter == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized void closeDatabase() {
mOpenCounter--;
if(mOpenCounter == 0) {
// Closing database
mDatabase.close();
}
}
}
Aşağıdaki gibi kullanın.
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way
Veritabanına her ihtiyacınız olduğunda DatabaseManager sınıfının openDatabase () yöntemini çağırmalısınız . Bu yöntemin içinde, veritabanının kaç kez açıldığını gösteren bir sayacımız var. Birine eşitse, yeni veritabanı bağlantısı oluşturmamız gerektiği anlamına gelir, eğer değilse, veritabanı bağlantısı zaten oluşturulmuş demektir.
Aynı şey closeDatabase () yönteminde de olur. Bu yöntemi her adlandırdığımızda sayaç azalır, sıfıra gittiğinde veritabanı bağlantısını kapatıyoruz.
Şimdi veritabanınızı kullanabilmeli ve iş parçacığının güvenli olduğundan emin olmalısınız.