2010-12-28 18 views
71

इस हफ्ते में डेटाबेस बंद मैं और ContentProvider के बारे में सभी सीखने किया गया है एक प्रदाता के अंदर निर्माण और डेटाबेस के उन्नयन का प्रबंधन करने के SQLiteOpenHelper वर्ग का उपयोग कर। विशेष रूप से, मैं एसडीके के नमूने निर्देशिका से नोटपैड उदाहरण के माध्यम से पढ़ रहा हूं।एक ContentProvider

अब, मैं देख सकता हूँ SQLiteOpenHelper एक करीबी() विधि है। मुझे पता है कि निष्क्रिय डेटाबेस खुला छोड़ बुरा व्यवहार है और मेमोरी लीक और whatnot पैदा कर सकता है (जब तक this चर्चा सही दिशा में आगे बढ़ रहा है) कर रहा हूँ। अगर मैं एक गतिविधि में कक्षा उपयोग कर रहे थे, तो मैं बस पास() विधि OnDestroy() में कहेंगे, लेकिन जहाँ तक मुझे पता है, ContentProvider ही जीवन चक्र गतिविधियों है कि नहीं है। नोटपैड के लिए कोड कभी नहीं पास() कॉल करने के लिए लगता है, इसलिए मुझे लगता है कि यह SQLiteOpenHelper या पहेली के कुछ अन्य टुकड़ा द्वारा नियंत्रित किया जाता चाहते हैं, लेकिन मैं वास्तव में निश्चित रूप से जानना चाहते हैं। मैं वास्तव में नमूना कोड विश्वास नहीं है कि ज्यादा है, या तो ...

प्रश्न सारांश: जब हम एक प्रदाता में डेटाबेस बंद हो जाना चाहिए, अगर सब पर?

+11

डियान हैकबर्न ने कहा कि [डीबी को बंद करने की कोई आवश्यकता नहीं है] (http://groups.google.com/d/msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ)। – bigstones

+1

यह इस धागे पर सबसे महत्वपूर्ण जानकारी है। मैंने इसे जवाब दिया। – philipp

उत्तर

88

According to Dianne Hackborn (एंड्रॉयड ढांचे इंजीनियर) एक सामग्री प्रदाता में डेटाबेस बंद करने के लिए कोई जरूरत नहीं है (यह करने के लिए सबसे आसान तरीका है सुनिश्चित करें कि आपके डेटाबेस का उपयोग तरीकों एक सिंक्रनाइज़ ब्लॉक में लिपटे रहे हैं बनाने के लिए है।)।

जब इसकी होस्टिंग प्रक्रिया बनाई गई है एक सामग्री प्रदाता बन जाता है, और जब तक प्रक्रिया करता है के लिए चारों ओर बनी हुई है, इसलिए करने की कोई जरूरत डेटाबेस बंद है - यह के भाग के रूप से बंद कर दिया जाएगा जब प्रक्रिया मारे जाती है तो कर्नेल प्रक्रिया के संसाधनों की सफाई करना।

धन्यवाद @ बिगस्टोन इसे इंगित करने के लिए धन्यवाद।

+5

धन्यवाद। बीटीडब्ल्यू, वे (एंड्रॉइड टीम) को इस "सरल" चीज को [दिशानिर्देश] (http://developer.android.com/guide/topics/providers/content-provider-creating.html) पर टिप्पणी करनी चाहिए, या कम से कम नमूना में नेट पर कोडर खोज करने की बजाए कोड। –

+14

मुझे याद नहीं है कि क्या गुम है :) – philipp

+3

ठीक है, लेकिन शट डाउन फ़ंक्शन क्या करता है। कोड है: सार्वजनिक शून्य शटडाउन() { Log.w (TAG, "सामग्रीप्रोवाइडर शटडाउन() को लागू करने के लिए सभी डेटाबेस सुनिश्चित करें" + "कनेक्शन शानदार ढंग से बंद हो जाते हैं"); } – ata

1

जब आप इसके साथ काम करते हैं तो इसे बंद करें, अधिमानतः अंत में ब्लॉक में ताकि आप यह सुनिश्चित कर सकें कि ऐसा होता है। मुझे लगता है कि एक छोटे से घिसे-पिटे और ऑफ-द-कफ लगता है, लेकिन यह वास्तव में केवल जवाब है कि मैं के बारे में पता है। यदि आप डेटाबेस खोलते हैं और कोई क्रिया करते हैं, तो जब आप किसी तथ्य के बारे में जानते हैं तो इसे तब तक बंद कर दें जब तक कि आपको किसी तथ्य की आवश्यकता न हो, फिर से इसकी आवश्यकता होगी (जिस स्थिति में इसकी आवश्यकता होने के बाद इसे बंद करना सुनिश्चित करें)।

+2

एकमात्र तरीका हम जान सकते हैं कि हमें फिर से इसकी आवश्यकता होगी प्रदाता के बाहर से (उस कोड में जो इसका उपयोग कर रहा है) से बाहर है। मुझे लगता है कि प्रदाता के अंदर, डेटाबेस हर बार प्राप्त होता है writeableDatabase() या getReadableDatabase() को SQLiteOpenHelper पर कॉल किया जाता है। आपके सुझाव के आधार पर, क्या मुझे प्रत्येक विधि में एक करीबी() बाद में जोड़ना चाहिए जहां इन्हें बुलाया जाता है? ऐसा लगता है कि कई प्रश्न एक दूसरे के बाद एक चलाए जाते हैं, तो वहां बहुत सारे डेटाबेस खोलने और बंद होने पर बंद हो जाएगा। मुझे यकीन नहीं है, लेकिन मुझे लगता है कि प्रदर्शन को प्रभावित करेगा। – SilithCrowe

0

आप एक गतिविधि के भीतर अपनी सामग्री प्रदाता का उपयोग कर रहे हैं, तो मुझे नहीं लगता कि आप सामग्री प्रदाता के संबंध बनाए रखने के लिए किया है। आप startManagingCursor का उपयोग करके वापस कर्सर ऑब्जेक्ट को प्रबंधित कर सकते हैं। गतिविधि की ऑन पॉज़ विधि में, आप सामग्री प्रदाता को छोड़ सकते हैं। (आप इसे रेज़्यूम में पुनः लोड कर सकते हैं)। यह मानते हुए कि गतिविधि जीवन चक्र आमतौर पर सीमित होगा, यह पर्याप्त होगा। (कम से कम मेरे हिसाब से;))

+0

बेशक यदि आप एसक्यूएल लाइट का उपयोग कर रहे हैं, तो आप परिणाम प्राप्त करने के बाद कनेक्शन बंद कर सकते हैं। (फिर से सुनिश्चित करें कि कर्सर की जीवन चक्र को startmanagingcursor का उपयोग करके गतिविधि द्वारा संभाला जाता है। –

7

आप जब इसे खोलने अपने डेटाबेस स्वचालित रूप से बंद करने के लिए आप एक CursorFactory प्रदान कर सकते हैं चाहते हैं:

mContext.openOrCreateDatabase(DB_NAME, SQLiteDatabase.OPEN_READWRITE, new LeaklessCursorFactory()); 

यहाँ वर्ग हैं:

public class LeaklessCursorFactory implements CursorFactory { 
    @Override 
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, 
     String editTable, SQLiteQuery query) { 
     return new LeaklessCursor(db,masterQuery,editTable,query); 
    } 
} 


public class LeaklessCursor extends SQLiteCursor { 
    static final String TAG = "LeaklessCursor"; 
    final SQLiteDatabase mDatabase; 

    public LeaklessCursor(SQLiteDatabase database, SQLiteCursorDriver driver, String table, SQLiteQuery query) { 
     super(database, driver, table, query); 
     mDatabase = database; 
    } 

    @Override 
    public void close() { 
     Log.d(TAG, "Closing LeaklessCursor: " + mDatabase.getPath()); 
     super.close(); 
     if (mDatabase != null) { 
      mDatabase.close(); 
     } 
    } 
} 
+1

दूसरों को ध्यान दें: कृपया प्लीरॉक का जवाब भी ध्यान दें, क्योंकि यह इस उत्तर को एक छोटे से लेकिन महत्वपूर्ण तरीके से अपडेट करता है। यह एक भयानक समाधान है, वैसे - मैं जब मैं डिज़ाइन पैटर्न का अच्छा उपयोग करता हूं तो हमेशा थोड़ी सी गड़बड़ी प्राप्त करें।: पी – SilithCrowe

13

Ive का पालन करें Mannaz के जवाब और देखा कि SQLiteCursor(database, driver, table, query); निर्माता मान्य नहीं है। तब मैं getDatabase() विधि मिल गया है और यह बजाय प्रयोग किया mDatabase सूचक की; और पिछड़े क्षमता

public class MyOpenHelper extends SQLiteOpenHelper { 
    public static final String TAG = "MyOpenHelper"; 

    public static final String DB_NAME = "myopenhelper.db"; 
    public static final int DB_VESRION = 1; 

    public MyOpenHelper(Context context) { 
     super(context, DB_NAME, new LeaklessCursorFactory(), DB_VESRION); 
    } 

    //... 
} 

public class LeaklessCursor extends SQLiteCursor { 
    static final String TAG = "LeaklessCursor"; 

    public LeaklessCursor(SQLiteDatabase db, SQLiteCursorDriver driver, 
      String editTable, SQLiteQuery query) { 
     super(db, driver, editTable, query); 
    } 

    @Override 
    public void close() { 
     final SQLiteDatabase db = getDatabase(); 
     super.close(); 
     if (db != null) { 
      Log.d(TAG, "Closing LeaklessCursor: " + db.getPath()); 
      db.close(); 
     } 
    } 
} 


public class LeaklessCursorFactory implements CursorFactory { 
    @Override 
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, 
     String editTable, SQLiteQuery query) { 
     return new LeaklessCursor(db,masterQuery,editTable,query); 
    } 
} 
+1

जब लोग पुराने प्रश्नों का उत्तर/अपडेट करने के लिए समय लेते हैं तो मुझे यह पसंद है। धन्यवाद Pleerock! ऐसा लगता है कि मुझे इसे फिर से देखने की जरूरत है ... – SilithCrowe

+0

YW। जिस तरह से मुझे हमारे लीकलेस कर्सर में कुछ "रिसाव" मिला। जब आप सामग्री प्रदाताओं का उपयोग कर रहे हैं तो यह प्रभावी नहीं है। उदाहरण के लिए, जब आप डेटाबेस में डेटा अपडेट कर रहे हैं, तो आपका कर्सर भी अपडेट हो जाएगा। इसलिए यह कर्सर बंद हो जाएगा और नया कर्सर खोला जाएगा। जब हमारा कर्सर बंद हो जाएगा, तो हमारा डेटाबेस भी बंद हो जाएगा। इससे कोई त्रुटि हो सकती है।उदाहरण के लिए: ContentProvider (डेटाबेस खोलता है) -> क्वेरी (कर्सर के लिए, डीबी का उपयोग करता है) -> अद्यतन (कोई भी डेटा, डीबी का उपयोग करता है) -> सूचित करता है-> पुराने कर्सर को बंद करता है (डीबी भी बंद करता है) -> नया कर्सर बनाता है (क्वेरी, डीबी का उपयोग करता है) और POOOOW त्रुटि -> डीबी बंद था, नया कर्सर – pleerock

+0

नहीं खोल सकता है अगर getDatabase() कॉल शून्य वापस लौटा सकता है तो Log.d (...) क्रैश हो सकता है, मैंने एक संपादन में डाल दिया जो लॉग को ले जाता है if-statement, और संदर्भ में getDatabase() डालता है, क्योंकि आप इसे 3 बार उपयोग करते हैं। आप if-statement के बाहर एक लॉग जोड़ सकते हैं जो आपको यह बताता है कि "डेटाबेस" ने डेटाबेस बंद कर दिया है, भले ही getDatabase() उपयोगी हो तो यह शून्य हो। –

21

यह प्रश्न थोड़ा पुराना है लेकिन अभी भी काफी प्रासंगिक है। ध्यान दें कि यदि आप चीजों को 'आधुनिक' तरीके से कर रहे हैं (उदाहरण के लिए लोडर मैनेजर का उपयोग करना और पृष्ठभूमि थ्रेड में सामग्री प्रदाता से पूछने के लिए कर्सर लोडर बनाना), तो सुनिश्चित करें कि आप अपने सामग्री प्रदाता कार्यान्वयन में db.close() पर कॉल न करें। मुझे कर्सर लोडर/AsyncTaskLoader से संबंधित सभी प्रकार की क्रैश मिल रही थी जब उसने पृष्ठभूमि थ्रेड में ContentProvider तक पहुंचने का प्रयास किया, जिसे db.close() कॉल को हटाकर हल किया गया था।

तो तुम दुर्घटनाओं कि इस तरह दिखेगा (Jelly Bean 4.1.1) में चला रहे हैं:

Caused by: java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed. 
    at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962) 
    at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:677) 
    at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348) 
    at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894) 
    at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834) 
    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62) 
    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143) 
    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133) 
    at android.content.ContentResolver.query(ContentResolver.java:388) 
    at android.content.ContentResolver.query(ContentResolver.java:313) 
    at com.hindsightlabs.paprika.loaders.GroceryListLoader.loadInBackground(GroceryListLoader.java:147) 
    at com.hindsightlabs.paprika.loaders.GroceryListLoader.loadInBackground(GroceryListLoader.java:1) 
    at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:240) 
    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:51) 
    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:40) 
    at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:123) 
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
    ... 4 more 

या यह (आईसीएस 4.0।4):

Caused by: java.lang.IllegalStateException: database /data/data/com.hindsightlabs.paprika/databases/Paprika.db (conn# 0) already closed 
    at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2215) 
    at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:436) 
    at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:422) 
    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:79) 
    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164) 
    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156) 
    at android.content.ContentResolver.query(ContentResolver.java:318) 
    at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:49) 
    at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:35) 
    at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:240) 
    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:51) 
    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:40) 
    at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:123) 
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
    ... 4 more 

या यदि आप LogCat में त्रुटि संदेश जो इस तरह दिखेगा देख रहे हैं:

Cursor: invalid statement in fillWindow() 

फिर अपने ContentProvider कार्यान्वयन की जाँच करें और सुनिश्चित करें कि आप डेटाबेस समय से पहले ही बंद हो रहा नहीं कर रहे हैं। this के अनुसार, जब भी प्रक्रिया को मार दिया जाता है तो ContentProvider स्वचालित रूप से साफ़ हो जाएगा, इसलिए आपको समय से पहले अपना डेटाबेस बंद करने की आवश्यकता नहीं है।

जिसके अनुसार, सुनिश्चित करें कि आप अभी भी सही ढंग कर रहे हैं: अपने कर्सर कि ContentProvider.query() से लौटाए जाते हैं समापन

  1. । (कर्सर लोडर/लोडर मैनेजर यह आपके लिए स्वचालित रूप से करता है, लेकिन यदि आप लोडरमेनर फ्रेमवर्क के बाहर सीधे पूछताछ कर रहे हैं, या आपने एक कस्टम कर्सरलोडर/असिनक टास्कलोडर सबक्लास लागू किया है, तो आपको यह सुनिश्चित करना होगा कि आप अपने कर्सर को साफ़ कर रहे हैं ठीक से।)
  2. अपने सामग्री प्रदाता को थ्रेड-सुरक्षित तरीके से कार्यान्वित करना।
संबंधित मुद्दे