2012-10-30 5 views
6

Przeczytałem wiele tematów dotyczących tego komunikatu o błędzie, ale nie mogę rozwiązać mojego problemu.Android: java.lang.IllegalStateException: baza danych xxx.db (conn # 0) jest już zamknięta

Mam aplikację w Google Play i otrzymuję raporty o błędach od użytkowników. Gdy próbuję aplikacji, wszystko działa poprawnie.

W aplikacji zarządzam dużą bazą danych z około 30 tabelami. Zamykam bazę danych w mojej głównej działalności naDestroy() i wszystkie kursory są zamykane po zakończeniu zapytania.

Naprawdę nie wiem, dlaczego od czasu do czasu użytkownicy otrzymują ten komunikat o błędzie.

Oto cały dziennik błędów:

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) 

I jeszcze jedno:

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: Jeden nowy błąd.

Dziś dostałam nowy błąd związany z dostępem do bazy danych:

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

Wystarczy jednak. Opis onDestroy() jest raczej niejasny. Istnieje więc przekonanie, że nie można go nazwać w nieoczywistych sytuacjach. Na przykład zobacz _comments_ do tej odpowiedzi: http://stackoverflow.com/a/9409517/1665128. Tak więc, jeśli nie możesz odtworzyć problemu, warto zamknąć zadania DB w innym "bardziej gwarantowanym" oddzwanianiu. I zobaczyć. –

+0

@ full.stack.ex, dziękuję za odpowiedź. Twój pomysł brzmi interesująco, ale gdzie powinienem nazwać put hten metodami bazy danych, w końcu może? –

+1

to zależy od twojego projektu, o którym niewiele wiem. Grając w Captain Banal, wygląda na to, że prawdziwy problem jest gdzieś w cyklu życia. Oznacza to, że tworzenie i niszczenie muszą być symetryczne. Tak więc onResume/onPause lub onStart/onStop będą dobrymi parami. Tak więc wydaje się, że jest on naCreate/onDestroy, chyba że istnieje coś takiego jak długa usługa i statyczne odwołanie do bazy danych lub coś podobnego. Odgałęzienia kodu, staram się rozmieścić te rzeczy symetrycznie, starannie małpa (i ludzi) testować, publikować i monitorować, będąc gotowy do opublikowania aktualizacji wycofywania. –

Odpowiedz

2

onDestroy() zostanie wywołany tylko wtedy, gdy aktywność zostanie zakończona/zniszczona/usunięta ze stosu. Po wyprowadzeniu z działania zostanie wywołany onStop(). Jeśli chcesz zachować obecny projekt, po prostu zadzwoń zamknij w onStop(). UWAGA: onStop bieżącej aktywności zostanie wywołany po onCreate/onRestart w ramach nowej/następnej aktywności.

Jeśli masz pojedynczą DB można użyć klasy SQLiteOpenHelper jak wspomniano przez pawelieba Ponadto można mieć odniesienie sqlite db w klasie aplikacji, a może po prostu użyć odniesienia we wszystkich działaniach, jak

((MyApplication)getApplication()).db

Po prostu otwórz bazę danych w onCreate() klasy Application i zamknij ją w onTerminate().

Możesz również rzucić okiem na inne odpowiedzi SO dla tego błędu. Było to już wiele razy zadawane.

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

Bardzo dziękuję @ Atrix1987, spróbuję Twojego rozwiązania. –

1

bazy danych SQLite ma natywny interfejs, który jest dostępny poprzez SQLiteDatabase - ta klasa dba o synchronizację dostępu do rodzimej SQLite z różnych wątków.
jest buforowany pod numerem SQLiteOpenHelper, który zapewnia instancję o numerze SQLiteDatabase. Jest to wzór singletowy.

Z mojego doświadczenia, powinieneś mieć jedną instancję SQLiteOpenHelper i pobrać z niej buforowany plik SQLiteDatabase. Wtedy nie musisz dbać o zamykanie/otwieranie SQLiteDatabase.

Jedna instancja SQLiteOpenHelper dla aplikacji jest wymagana, aby mieć jedną instancję z SQLiteDatabase w aplikacji. Rozwiązuje problemy z wielowątkowym dostępem do natywnej bazy danych.

W skrócie: singleton z SQLiteOpenHelper, z którego otrzymujesz bazę danych dla każdego zapytania.
Używam Roboguice, aby poradzić sobie z brzydkim wzorem singleton.

+0

Dziękuję @ pawelzieba. Ok, więc muszę utworzyć nowy obiekt SQLiteOpenHelper w każdym działaniu, w którym muszę uzyskać dostęp do bazy danych? Dobrze? –

+0

Nie, mam na myśli singleton 'SQLiteOpenHelper' dla całej aplikacji. Najprostszym sposobem, w twoim przypadku, może być zaimplementowanie go w klasie 'android.app.Application', jak sądzę. – pawelzieba

+0

Ok, więc muszę użyć czegoś podobnego do tego: http://nerdwa.com/index.php/2011/09/database-sqliteopenhelper-singletlet-class-for-android/, a następnie zainicjować obiekt, kiedy potrzebuję? –

1

Miałem podobny problem w projekcie, nad którym pracowałem.

W projekcie wykorzystano warstwę dostępu do danych (DAL), która była klasą abstrakcyjną ze statycznymi metodami używanymi do pobierania danych z bazy danych tylko do odczytu. Metody otworzyły bazę danych, pobrały dane i zamknęły bazę danych. Doprowadziło to do zgłaszania wyjątku podczas zamykania bazy danych. Nie zawsze i nie na wszystkich telefonach.

Problem zniknął po wdrożeniu content provider i użyłem go zamiast DAL.

+0

Dziękuję bardzo za sugestię. Przyjrzę się temu i spróbuję przez kilka dni sprawdzić, czy są jakieś błędy. –

0

pamiętaj

  • bliską cusor po kwerendy, aby uzyskać dane z SQLite.

  • użyć transakcji w aktualizacji, wstaw do sqlite.

Ale jakiś czas może używamy wielu JOIN w sekwencji zapytań, stworzyła tabelę świątyni do zapytań i może nie zamknąć natychmiast