Android SQLite DB कब बंद करना है


96

मैं Android पर SQLite डेटाबेस के साथ काम कर रहा हूं। मेरा डेटाबेस मैनेजर एक सिंगलटन है और अभी यह डेटाबेस के लिए एक कनेक्शन खोलता है जब इसे इनिशियलाइज़ किया जाता है। डेटाबेस को पूरे समय खुला छोड़ना सुरक्षित है ताकि जब कोई मेरी कक्षा को पहले से खुले डेटाबेस के साथ काम करने के लिए बुलाए? या क्या मुझे प्रत्येक एक्सेस की आवश्यकता से पहले और बाद में डेटाबेस को खोलना और बंद करना चाहिए। क्या पूरे समय खुला रहने में कोई नुकसान है?

धन्यवाद!

जवाबों:


60

मैं इसे पूरे समय खुला रखूंगा, और इसे कुछ जीवनचक्र विधि जैसे कि onStopया में बंद कर दूंगा onDestroy। इस तरह, आप आसानी से जाँच कर सकते हैं कि डेटाबेस पहले से ही कॉल करके isDbLockedByCurrentThreadया हर बार उपयोग करने से पहले isDbLockedByOtherThreadsएकल SQLiteDatabaseऑब्जेक्ट पर उपयोग में है या नहीं। यह डेटाबेस में कई जोड़तोड़ को रोकेगा और आपके एप्लिकेशन को संभावित क्रैश से बचाएगा

तो आपके सिंगलटन में, आपकी एकल SQLiteOpenHelperवस्तु प्राप्त करने के लिए आपके पास एक तरीका हो सकता है :

private SQLiteDatabase db;
private MyDBOpenHelper mySingletonHelperField;
public MyDBOpenHelper getDbHelper() {
    db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
    while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
        //db is locked, keep looping
    }
    return mySingletonHelperField;
}

इसलिए जब भी आप अपनी ओपन हेल्पर ऑब्जेक्ट का उपयोग करना चाहते हैं, तो इस गेट्टर विधि को कॉल करें (सुनिश्चित करें कि यह थ्रेडेड है)

आपके सिंगलटन में एक और तरीका हो सकता है (ऊपर दिए गए गेट को कॉल करने का प्रयास करने से पहले हर समय कहा जाता है):

public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
    if(null == this.mySingletonHelperField) {
        this.mySingletonHelperField = mySingletonHelperField;
        this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
    }
}

आप एकल में डेटाबेस को बंद करना चाहते हैं:

public void finalize() throws Throwable {
    if(null != mySingletonHelperField)
        mySingletonHelperField.close();
    if(null != db)
        db.close();
    super.finalize();
}

यदि आपके एप्लिकेशन के उपयोगकर्ताओं में बहुत तेज़ी से कई डेटाबेस इंटरैक्शन बनाने की क्षमता है, तो आपको कुछ ऐसा उपयोग करना चाहिए जैसे मैंने ऊपर दिखाया है। लेकिन अगर कोई न्यूनतम डेटाबेस इंटरैक्शन है, तो मैं इसके बारे में चिंता नहीं करूंगा, और हर बार डेटाबेस बनाएं और बंद करें।


मैं इस दृष्टिकोण (डेटाबेस खुला रखने) के साथ 2 के एक कारक द्वारा डेटाबेस का उपयोग तेज कर सकता था, धन्यवाद
teh.fonsi

2
कताई एक बहुत ही खराब तकनीक है। नीचे मेरा जवाब देखें।
मिश्रणेल

1
@ मिकसेल अच्छा बिंदु। मेरा मानना है कि मैं इस उत्तर पोस्ट से पहले एपीआई 16 उपलब्ध था, लेकिन मैं गलत हो सकता है
जेम्स

1
@binnyb मुझे लगता है कि अपने उत्तर को अपडेट करना बेहतर है ताकि यह लोगों को गुमराह न करे।
मिश्रण

3
WARN isDbLockedByOtherThreads () सत्यापित है और एपीआई 16 के बाद से वापस लौटता है। इसके अलावा isDbLockedByCurrentThread () चेकिंग अनंत लूपिंग देगी क्योंकि यह वर्तमान थ्रेड को रोकता है और कुछ भी 'DB को अनलॉक नहीं' कर सकता है ताकि यह विधि सही हो जाए।
Matreshkin

20

चूंकि डेटाबेस को किसी अन्य थ्रेड द्वारा लॉक किया गया है, इसलिए अब जाँचने की कोई आवश्यकता नहीं है। जबकि आप सुरक्षित हैं हर धागे में सिंगलटन SQLiteOpenHelper का उपयोग करते हैं। से isDbLockedByCurrentThreadप्रलेखन:

इस विधि का नाम उस समय से आता है जब डेटाबेस से एक सक्रिय संबंध होने का मतलब था कि थ्रेड डेटाबेस पर एक वास्तविक लॉक पकड़ रहा था। आजकल, वहाँ एक सच "डेटाबेस लॉक" नहीं है, हालांकि थ्रेड ब्लॉक हो सकते हैं यदि वे किसी विशेष ऑपरेशन को करने के लिए डेटाबेस कनेक्शन प्राप्त नहीं कर सकते हैं।

isDbLockedByOtherThreads एपीआई स्तर 16 के बाद से पदावनत किया गया है।


प्रति सूत्र एक उदाहरण का उपयोग करने का कोई मतलब नहीं है। SQLiteOpenHelper धागा सुरक्षित है। यह भी बहुत स्मृति अक्षम है। इसके बजाय एक एप्लिकेशन को SQLiteOpenHelper प्रति डेटाबेस का एक भी उदाहरण रखना चाहिए। बेहतर संगामिति के लिए, WriteAheadLogging का उपयोग करने की अनुशंसा की जाती है जो 4 कनेक्शनों के लिए कनेक्शन पूलिंग प्रदान करता है।
ईजेबॉय

@ejboy मेरा मतलब था कि। "प्रत्येक थ्रेड में सिंगलटन (= एक) SQLiteOpenHelper का उपयोग करें", "प्रति थ्रेड" नहीं।
मिश्रण

15

सवालों के बारे में:

मेरा डेटाबेस मैनेजर एक सिंगलटन है और अभी यह डेटाबेस के लिए एक कनेक्शन खोलता है जब इसे इनिशियलाइज़ किया जाता है।

हमें 'ओपनिंग डीबी', 'ओपनिंग ए कनेक्शन' को विभाजित करना चाहिए। SQLiteOpenHelper.getWritableDatabase () एक खोला DB देता है। लेकिन हमें कनेक्शनों को नियंत्रित करने की आवश्यकता नहीं है क्योंकि यह आंतरिक रूप से किया जाता है।

डेटाबेस को पूरे समय खुला छोड़ना सुरक्षित है ताकि जब कोई मेरी कक्षा को पहले से खुले डेटाबेस के साथ काम करने के लिए बुलाए?

हाँ यही है। लेन-देन ठीक से बंद होने पर कनेक्शन लटका नहीं है। ध्यान दें कि यदि GC इसे अंतिम रूप देता है तो आपका DB भी स्वतः बंद हो जाएगा।

या क्या मुझे प्रत्येक एक्सेस की आवश्यकता से पहले और बाद में डेटाबेस को खोलना और बंद करना चाहिए।

SQLiteDatabase उदाहरण बंद करना कनेक्शन को बंद करने के अलावा कुछ भी नहीं देता है लेकिन इस समय कुछ कनेक्शन हैं तो यह एक डेवलपर का बुरा है। SQLiteDatabase.close (), SQLiteOpenHelper.getWritableDatabase () के बाद एक नया उदाहरण लौटाएगा।

क्या पूरे समय खुला रहने में कोई नुकसान है?

नहीं, वहाँ नहीं है। यह भी ध्यान दें कि DB को एक असंबंधित क्षण में बंद करना और उदाहरण के लिए Activity.onStop () में सक्रिय कनेक्शन बंद कर सकते हैं और असंगत स्थिति में डेटा छोड़ सकते हैं।


धन्यवाद, आपके शब्दों को पढ़ने के बाद "SQLiteDatabase.close (), SQLiteOpenHelper.getWritableDatabase () एक नया उदाहरण लौटाएगा" मुझे एहसास हुआ कि आखिरकार मेरे पास मेरे आवेदन की एक पुरानी समस्या का जवाब है। एक समस्या के लिए, जो एंड्रॉइड 9 के प्रवास के बाद महत्वपूर्ण हो गई (देखें stackoverflow.com/a/54224922/297710 )
yvolk

1

Android 8.1 में एक SQLiteOpenHelper.setIdleConnectionTimeout(long)विधि है जो:

मिलिसेकंड की अधिकतम संख्या सेट करता है जो कि SQLite कनेक्शन को बंद होने और पूल से निकालने से पहले निष्क्रिय होने की अनुमति देता है।

https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#setIdleConnectionTimeout(long)


3
इन दिनों यह विकृत हो गया है।
मैट्रेसकिन

1

प्रदर्शन के दृष्टिकोण से इष्टतम तरीका है कि एप्लिकेशन स्तर पर SQLiteOpenHelper का एक भी उदाहरण रखा जा सके। डेटाबेस खोलना महंगा हो सकता है और एक ब्लॉकिंग ऑपरेशन है, इसलिए इसे मुख्य थ्रेड और / या गतिविधि जीवनचक्र विधियों पर नहीं किया जाना चाहिए।

setIdleConnectionTimeout () विधि (एंड्रॉइड 8.1 में पेश की गई) को डेटाबेस का उपयोग नहीं करने पर रैम को मुक्त करने के लिए इस्तेमाल किया जा सकता है। यदि निष्क्रिय समय समाप्त हो गया है, तो डेटाबेस कनेक्शन (ओं) को निष्क्रियता की अवधि के बाद बंद कर दिया जाएगा, अर्थात जब डेटाबेस एक्सेस नहीं किया गया था। जब कोई नई क्वेरी निष्पादित की जाती है, तो कनेक्शन को पारदर्शी रूप से ऐप में फिर से खोला जाएगा।

इसके अलावा, जब कोई ऐप रिलीज़ करता है तो मीमोरी () बैकग्राउंड में चला जाता है या मेमोरी प्रेशर का पता लगाता है, जैसे ऑनट्रिमोरी () में



-9

अपना स्वयं का एप्लिकेशन संदर्भ बनाएं, फिर वहां से डेटाबेस खोलें और बंद करें। उस ऑब्जेक्ट में एक OnTerminate () विधि भी है जिसका उपयोग आप कनेक्शन को बंद करने के लिए कर सकते हैं। मैंने इसकी कोशिश अभी तक की है लेकिन यह एक बेहतर तरीका है।

@binnyb: मुझे कनेक्शन बंद करने के लिए अंतिम रूप () का उपयोग करना पसंद नहीं है। काम हो सकता है, लेकिन जो मैं जावा कोडाइज () विधि में लिखने के कोड को समझता हूं वह एक बुरा विचार है।


आप अंतिम रूप पर भरोसा नहीं करना चाहते क्योंकि आप कभी नहीं जानते कि इसे कब बुलाया जाएगा। जब तक जीसी इसे साफ करने का फैसला नहीं करता, तब तक कोई वस्तु अधर में रह सकती है, और उसके बाद ही अंतिम रूप () कहा जाएगा। इसलिए यह बेकार है। इसे करने का सही तरीका एक विधि है जिसे तब कहा जाता है जब ऑब्जेक्ट का उपयोग नहीं किया जाता है (चाहे कचरा एकत्र किया जा रहा है या नहीं), और यही वास्तव में onTerminate () है।
इरवान

5
डॉक्स का स्पष्ट रूप से कहना है कि onTerminateउत्पादन के माहौल में नहीं बुलाया जाएगा - developer.android.com/reference/android/app/…
Eliezer
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.