Akses Database Bersamaan
Artikel yang sama di blog saya (saya lebih suka memformat)
Saya menulis artikel kecil yang menjelaskan cara membuat akses ke utas basis data android Anda aman.
Dengan asumsi Anda memiliki SQLiteOpenHelper Anda sendiri .
public class DatabaseHelper extends SQLiteOpenHelper { ... }
Sekarang Anda ingin menulis data ke basis data di utas terpisah.
// 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();
Anda akan mendapatkan pesan berikut di logcat Anda dan salah satu perubahan Anda tidak akan ditulis.
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
Ini terjadi karena setiap kali Anda membuat objek SQLiteOpenHelper baru, Anda sebenarnya membuat koneksi database baru. Jika Anda mencoba menulis ke database dari koneksi berbeda yang sebenarnya pada saat yang sama, orang akan gagal. (dari jawaban di atas)
Untuk menggunakan basis data dengan banyak utas, kami perlu memastikan bahwa kami menggunakan satu koneksi basis data.
Mari kita membuat Database Manager kelas tunggal yang akan menahan dan mengembalikan objek SQLiteOpenHelper tunggal .
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();
}
}
Kode yang diperbarui yang menulis data ke basis data dalam utas terpisah akan terlihat seperti ini.
// 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();
Ini akan membuat Anda crash lagi.
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
Karena kita hanya menggunakan satu koneksi basis data, metode getDatabase () mengembalikan instance objek SQLiteDatabase yang sama untuk Thread1 dan Thread2 . Apa yang terjadi, Thread1 dapat menutup basis data, sementara Thread2 masih menggunakannya. Itu sebabnya kami mengalami crash IllegalStateException .
Kita perlu memastikan tidak ada yang menggunakan database dan baru kemudian menutupnya. Beberapa orang di stackoveflow disarankan untuk tidak pernah menutup SQLiteDatabase Anda . Ini akan menghasilkan pesan logcat berikut.
Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed
Sampel kerja
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();
}
}
}
Gunakan sebagai berikut.
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way
Setiap kali Anda membutuhkan database, Anda harus memanggil metode openDatabase () dari kelas DatabaseManager . Di dalam metode ini, kami memiliki penghitung, yang menunjukkan berapa kali database dibuka. Jika sama dengan satu, itu berarti kita perlu membuat koneksi database baru, jika tidak, koneksi database sudah dibuat.
Hal yang sama terjadi pada metode closeDatabase () . Setiap kali kita memanggil metode ini, penghitung berkurang, setiap kali menjadi nol, kita menutup koneksi basis data.
Sekarang Anda harus dapat menggunakan database Anda dan pastikan itu aman.