2012-10-30 9 views
6

Ho letto molti argomenti su questo messaggio di errore ma non riesco a risolvere il mio problema.Android: java.lang.IllegalStateException: database xxx.db (conn # 0) già chiuso

Ho un'app su Google Play e ricevo alcuni segnalazioni di errori dagli utenti. Quando provo l'app, tutto funziona correttamente.

Nell'applicazione, sto gestendo un grande database con qualcosa come 30 tabelle. Sto chiudendo il database nella mia attività principale suDestroy() e tutti i cursori sono chiusi al termine della query.

Non so davvero perché di tanto in tanto gli utenti ricevano questo messaggio di errore.

Ecco l'intero registro di errore:

java.lang.IllegalStateException: database /data/data/mdpi.android/databases/LocalDatabase.db (conn# 0) already closed 
at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2213) 
at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1565) 
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1525) 
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1605) 
at mdpi.android.database.LocalDatabase.getHistoryLastSuccessfullUpdate(LocalDatabase.java:661) 
at mdpi.android.Journals$7.onItemClick(Journals.java:723) 
at android.widget.AdapterView.performItemClick(AdapterView.java:292) 
at android.widget.Gallery.onSingleTapUp(Gallery.java:960) 
at android.view.GestureDetector.onTouchEvent(GestureDetector.java:1310) 
at android.widget.Gallery.onTouchEvent(Gallery.java:937) 
at android.view.View.dispatchTouchEvent(View.java:5724) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1964) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1725) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739) 
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970) 
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739) 
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2071) 
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1405) 
at android.app.Activity.dispatchTouchEvent(Activity.java:2426) 
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2019) 
at android.view.View.dispatchPointerEvent(View.java:5904) 
at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3155) 
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2670) 
at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:1000) 
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2679) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:137) 
at android.app.ActivityThread.main(ActivityThread.java:4517) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:511) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760) 
at dalvik.system.NativeStart.main(Native Method) 

E un altro:

java.lang.RuntimeException: Unable to start activity ComponentInfo{mdpi.android/mdpi.android.UserInformations}: java.lang.IllegalStateException: database /data/data/mdpi.android/databases/LocalDatabase.db (conn# 0) already closed 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2202) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2237) 
at android.app.ActivityThread.access$600(ActivityThread.java:139) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:4974) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:511) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 
at dalvik.system.NativeStart.main(Native Method) 
Caused by: java.lang.IllegalStateException: database /data/data/mdpi.android/databases/LocalDatabase.db (conn# 0) already closed 
at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2194) 
at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1536) 
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1496) 
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1576) 
at mdpi.android.database.LocalDatabase.getUserInformations(LocalDatabase.java:357) 
at mdpi.android.UserInformations.onCreate(UserInformations.java:122) 
at android.app.Activity.performCreate(Activity.java:4538) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1071) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2158) 
... 11 more 

EDIT: Un nuovo errore.

Oggi, ho ottenuto un nuovo errore relativo al l'accesso al database:

java.lang.RuntimeException: An error occured while executing doInBackground() 

at android.os.AsyncTask$3.done(AsyncTask.java:278) 
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 
at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 
at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
at java.lang.Thread.run(Thread.java:856) 
Caused by: java.lang.NullPointerException 
at android.database.sqlite.SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290) 
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:115) 
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1718) 
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1591) 
at mdpi.android.database.LocalDatabase.insertCountry(LocalDatabase.java:143) 
at mdpi.android.database.CountryTable.EnterCountry(CountryTable.java:21) 
at mdpi.android.UserInformations$insertCountryAsync.doInBackground(UserInformations.java:270) 
at mdpi.android.UserInformations$insertCountryAsync.doInBackground(UserInformations.java:1) 
at android.os.AsyncTask$2.call(AsyncTask.java:264) 
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
... 4 more 
+0

Solo un però. La descrizione di onDestroy() è piuttosto vaga. Quindi c'è la convinzione che non possa essere chiamato in alcune situazioni non ovvie. Ad esempio, consulta _commenti_ per questa risposta: http://stackoverflow.com/a/9409517/1665128. Quindi, se non riesci a riprodurre il problema, potrebbe essere utile chiudere le cose del DB in un altro, callback "più garantito". E vedi. –

+0

@ full.stack.ex, grazie per la risposta. La tua idea sembra interessante, ma dove dovrei chiamare inserire i metodi del database, forse? –

+1

dipende dal tuo design di cui so poco. Giocando a Capitan Banal, sembra che il vero problema sia in un altro momento _open_ da qualche parte nel ciclo di vita. Significa che la creazione e la distruzione devono essere simmetriche. Quindi onResume/onPause o onStart/onStop sarebbero buone coppie. E così sembra essere onCreate/onDestroy a meno che non ci sia qualcosa come un servizio di lunga vita più un riferimento statico al DB o qualcosa del genere. Sostituire il codice, fare del mio meglio per posizionare simmetricamente quelle cose, testare attentamente (e umano-) scimmie, pubblicare e monitorare, essendo pronto a pubblicare un aggiornamento di rollback. –

risposta

2

onDestroy() verrà chiamato solo quando l'attività è completata/distrutta/rimossa dallo stack. Quando si esce dall'attività, verrà chiamato il numero onStop(). Se hai bisogno di mantenere il tuo attuale design, basta chiamare close in onStop(). NOTA: onStop dell'attività corrente verrà chiamato dopo onCreate/onRestart dell'attività nuova/successiva.

Se si dispone di un unico DB è possibile utilizzare una classe SQLiteOpenHelper come detto da pawelieba Inoltre è possibile avere il riferimento sqlite db nella classe Application, e si può semplicemente utilizzare il riferimento a tutte le attività, come

((MyApplication)getApplication()).db

Basta aprire il DB in onCreate() della classe Applicazione e chiuderlo in onTerminate().

Puoi anche dare un'occhiata ad altre risposte SO per questo errore. È stato chiesto un sacco di volte prima.

java.lang.illegalstateexception database not open android

Android java.lang.IllegalStateException database already closed

Android insert the data SQLite error Caused by java.lang.IllegalStateException: database not open

+0

Grazie mille @ Atrix1987, proverò la soluzione. –

1

database SQLite ha un'interfaccia nativa che è accessibile attraverso SQLiteDatabase - questa classe si prende cura per sincronizzare l'accesso al SQLite nativo da diversi thread.
SQLiteDatabase è memorizzato nella cache in SQLiteOpenHelper che si presta per fornire istanza di SQLiteDatabase, è il modello singleton.

Dalla mia esperienza, si dovrebbe avere un'istanza di SQLiteOpenHelper e ottenerlo nella cache SQLiteDatabase da esso. Quindi non è necessario aver cura di chiudere/aprire SQLiteDatabase.

Un'istanza di SQLiteOpenHelper per l'applicazione è necessaria per disporre di un'istanza singola di SQLiteDatabase nell'app. Risolverà i problemi con l'accesso multi-threaded al database nativo.

In breve: singleton di SQLiteOpenHelper da cui si ottiene il database per ogni query.
Sto usando Roboguice per gestire il brutto modello di singleton.

+0

Grazie @pawelzieba. Ok, quindi devo creare un nuovo oggetto SQLiteOpenHelper in ogni attività in cui ho bisogno di accedere al database? Destra? –

+0

No, intendo avere un singleton di 'SQLiteOpenHelper' per l'intera app. Il modo più semplice, nel tuo caso, potrebbe essere quello di implementarlo nella classe 'android.app.Application', penso. – pawelzieba

+0

Ok, quindi devo usare qualcosa di simile a questo: http://nerdwa.com/index.php/2011/09/database-sqliteopenhelper-singleton-class-for-android/ e poi instanciate l'oggetto quando ho bisogno? –

1

Ho avuto un problema simile in un progetto su cui ho lavorato.

Il progetto utilizzava un Data Access Layer (DAL) che era una classe astratta con metodi statici usati per recuperare dati da un database di sola lettura. I metodi hanno aperto un database, recuperato i dati e chiuso il db. Ciò ha portato a un'eccezione generata quando il db veniva chiuso. Non sempre e non su tutti i telefoni.

Il problema è andato via quando ho implementato uno content provider e lo ho usato al posto del DAL.

+0

Grazie mille per il suggerimento. Darò un'occhiata a questo e proveremo per un paio di giorni per vedere se ci sono errori. –

0

Assicurarsi

  • vicino cusor dopo query per ottenere dati da SQLite.

  • utilizzare la transazione in aggiornamento, inserire in sqlite.

Ma qualche tempo forse usiamo molti JOIN in sequenze di query, ha creato tavolo tempio per eseguire query e forse non ha chiuso subito

Problemi correlati