समवर्ती डेटाबेस एक्सेस
मेरे ब्लॉग पर एक ही लेख (मुझे अधिक प्रारूपण पसंद है)
मैंने छोटा लेख लिखा है जो आपके एंड्रॉइड डेटाबेस थ्रेड को सुरक्षित बनाने के तरीके का वर्णन करता है।
मान लें कि आपके पास अपनी खुद की 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();
आपको अपने लॉगकैट में निम्न संदेश मिलेगा और आपका कोई भी परिवर्तन नहीं लिखा जाएगा।
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
ऐसा इसलिए हो रहा है क्योंकि हर बार जब आप नई SQLiteOpenHelper ऑब्जेक्ट बनाते हैं तो आप वास्तव में नया डेटाबेस कनेक्शन बना रहे होते हैं। यदि आप एक ही समय में वास्तविक अलग-अलग कनेक्शन से डेटाबेस में लिखने का प्रयास करते हैं, तो एक असफल हो जाएगा। (उपरोक्त उत्तर से)
एक से अधिक थ्रेड वाले डेटाबेस का उपयोग करने के लिए हमें यह सुनिश्चित करना होगा कि हम एक डेटाबेस कनेक्शन का उपयोग कर रहे हैं।
आइए सिंगलटन क्लास डेटाबेस मैनेजर बनाते हैं जो एकल SQLiteOpenHelper ऑब्जेक्ट को रखेगा और वापस लौटाएगा ।
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();
}
}
अद्यतित कोड जो अलग थ्रेड्स में डेटाबेस में डेटा लिखते हैं, वह इस तरह दिखेगा।
// 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();
यह आपको एक और दुर्घटना लाएगा।
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
हम केवल एक डेटाबेस कनेक्शन का उपयोग कर रहे हैं, विधि getDatabase () का एक ही उदाहरण लौट SQLiteDatabase के लिए वस्तु Thread1 और Thread2 । क्या हो रहा है, थ्रेड 1 डेटाबेस को बंद कर सकता है, जबकि थ्रेड 2 अभी भी इसका उपयोग कर रहा है। इसलिए हमारे पास IllegalStateException क्रैश है।
हमें यह सुनिश्चित करने की आवश्यकता है कि कोई भी डेटाबेस का उपयोग नहीं कर रहा है और केवल तब इसे बंद करें। स्टैकविस्टफ़्लो पर कुछ लोगों ने आपके SQLiteDatabase को कभी बंद नहीं करने की सिफारिश की । यह लॉगकोट संदेश का अनुसरण करेगा।
Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed
काम का नमूना
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();
}
}
}
इसे निम्नानुसार उपयोग करें।
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way
हर बार जब आप डेटाबेस की जरूरत है आप बुलाना चाहिए openDatabase () की विधि DatabaseManager वर्ग। इस पद्धति के अंदर, हमारे पास एक काउंटर है, जो इंगित करता है कि कितनी बार डेटाबेस खोला गया है। यदि यह एक के बराबर है, तो इसका मतलब है कि हमें नया डेटाबेस कनेक्शन बनाने की आवश्यकता है, यदि नहीं, तो डेटाबेस कनेक्शन पहले से ही बनाया गया है।
ऐसा ही closeDatabase () मेथड मे होता है । जब भी हम इस विधि को कहते हैं, काउंटर कम हो जाता है, जब भी यह शून्य पर जाता है, हम डेटाबेस कनेक्शन को बंद कर रहे हैं।
अब आपको अपने डेटाबेस का उपयोग करने में सक्षम होना चाहिए और सुनिश्चित करें कि यह थ्रेड सुरक्षित है।