2009-09-27 10 views
42

W maju opublikowałem this w grupie dyskusyjnej [android-developers] Google. Nigdy nie słyszałam odpowiedzi i nie udało mi się odtworzyć problemu, dopóki jeden z moich studentów nie zrobił tego w zeszłym tygodniu. Pomyślałem, że zostawię to tutaj i zobaczę, czy zadzwoni jakiemukolwiek dzwonowi.Wyjątek: próba uzyskania odniesienia do bliskiego SQLiteClosable

W jednym z próbki kodu, mam następujące metody:

static Cursor getAll(SQLiteDatabase db, String orderBy) { 
     return(db.rawQuery("SELECT * FROM restaurants "+orderBy, null)); 

} 

Podczas uruchamiania go sporadycznie, otrzymuję to:

05-01 14:45:05.849: ERROR/AndroidRuntime(1145): 
java.lang.IllegalStateException: attempt to acquire a reference on a 
close SQLiteClosable 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:31) 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:56) 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:49) 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49) 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1118) 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1092) 
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):  at 
apt.tutorial.Restaurant.getAll(Restaurant.java:14) 

To nie ma sensu mnie. Baza danych jest zdecydowanie otwarta. SQLiteClosable jest SQLiteQuery stworzony przez SQLiteQueryDriver i widzę żadnych dowodów, że istnieje pula przedmiot lub coś się dzieje tutaj które mogłyby wyjaśnić, jak „nowy” SQLiteClosable jest już zamknięta. The Fakt, że jest on sporadyczny (co oznacza, że ​​te same operacje interfejsu użytkownika, czasami uruchamiają wyjątek, ale nie zawsze) sugeruje jakiś rodzaj puli, wyścigu lub coś takiego ... ale nie jestem pewien gdzie.

Myśli?

Dzięki!

AKTUALIZACJA: Kod, o którym mowa, pochodzi z podręczników LunchList z mojej książki Android Programming Tutorials. Jest nieco rozłożony i niezbyt dobrze nadaje się do publikowania bezpośrednio w SO. Możesz pobrać kod tej książki z powyższego linku, jeśli chcesz go obejrzeć. Nie pamiętam dokładnie, która edycja samouczka, nad którym pracował student w tym czasie, choć była w zakresie 12-tutorialowego kursu Tutorial 16. Miałem przede wszystkim nadzieję, że natknę się na kogoś, kto wcześniej potknął się o ten problem i miał prawdopodobnego winowajcę. Jestem prawie pewien, że moja baza danych jest otwarta. Dzięki jeszcze raz!

+0

Czy zrobiłeś postęp w tej sprawie? –

+0

Zobacz aktualizację, którą właśnie dodałem do pierwotnego zapytania. To takie rzadkie zjawisko, że nie martwię się zbytnio; Miałem tylko nadzieję, że ktoś, kto wcześniej miał problem, zobaczy pytanie. Dzięki! – CommonsWare

+0

Tak, to też zdarza się bardzo rzadko. Wciąż spodziewałbym się przynajmniej znaleźć raport o błędzie, ale tego nie zrobiłem. –

Odpowiedz

50

Ten doprowadzał mnie do szaleństwa przez najdłuższy czas. Rozwiązanie, które znalazłem jest dość proste: nie przechodź do obiektów o numerach SQLiteDatabase. Zamiast tego użyj numeru SQLiteOpenHelper i zadzwoń pod numer getWritableDatabase() za każdym razem, gdy go potrzebujesz. Od docs:

publicznych zsynchronizowane SQLiteDatabase getWritableDatabase()

tworzyć i/lub otworzyć bazę danych, która będzie używana do czytania i pisania. Po pomyślnym otwarciu baza danych jest zapisywana w pamięci podręcznej, więc można wywoływać tę metodę za każdym razem, gdy trzeba pisać do bazy danych.

Odpowiedź była tam przez cały czas.

+1

Hmmmm ... to bardzo interesujące. Wygląda na to, że możesz wywołać 'close()' na SQLiteOpenHelper, aby zamknąć bazę danych. Będę musiał eksperymentować z tym. Wielkie dzięki! – CommonsWare

+0

Serdecznie witamy! Dziękuję za zadanie pytania; pomógł mi znaleźć rozwiązanie. –

+0

Nie do końca rozumiem. Czy możesz zamieścić bardziej rozbudowany przykład? – Codevalley

1

Miałem ten sam problem przez kilka dni, a moim rozwiązaniem było umieszczenie metody open() tuż przed moim zapytaniem i metodą close() po operacji bazy danych. Wygląda to mniej więcej tak.

open(); 
Cursor cur=db.query(DATABASE_TABLE_CON, null, null, null, null, null, " name ASC"); 
close(); 
return cur; 

Działa to dobrze, ale jestem zaniepokojony kosztami zasobów. Nie jestem pewien, czy wydaję więcej zasobów z całą tą otwierającą i zamykającą bazą danych przed jakimkolwiek działaniem.

1

Mam podobny problem, mimo że już podążam za Jarett's advice.W moim przypadku problem zdarza się dość regularnie w przypadku zmian orientacji. Odkryłem, że z jakiegoś powodu nie doszedłem jeszcze do sedna, mój kod generuje dwa identyczne, niemal równoczesne AsyncTasks na zmianie orientacji (w przeciwieństwie do jednego, gdy aktywność zaczyna się normalnie). Zadania te wykonują tę samą kwerendę bazy danych w tym samym czasie z różnych wątków.

Ten wyjątek (lub czasami inny wyjątek SQLiteException) jest wynikiem. Wygląda więc na to, że ten komunikat może być symptomem problemów z współbieżnością, nawet jeśli nie jest to główny problem z zamieszczonego tutaj problemu.

+0

Po zmianie orientacji ekranu tworzona jest cała nowa aktywność. Jeśli uruchamiasz AsyncTasks w metodzie onCreate() działania, zostaną utworzone dwie. –

+0

Jeśli poprawnie rozszerzasz 'SQLiteOpenHelper' i zamykasz bazę danych w' onDestroy() ', to nie powinieneś mieć żadnych problemów z jednoczesnymi wywołaniami w celu modyfikacji bazy danych. –

8

Baza danych SQLiteDatabase jest zamykana automatycznie w pewnych warunkach.

http://darutk-oboegaki.blogspot.com/2011/03/sqlitedatabase-is-closed-automatically.html

getCount() i onMove() w kursora wywołać faktyczny zapytania w SQLiteQuery. Po uzyskaniu wszystkich wymaganych danych SQLiteQuery zmniejsza liczbę odwołań do instancji SQLiteDatabase. Gdy licznik odniesień osiągnie 0, baza danych zostanie zamknięta.

Należy zauważyć, że zapytanie może być wykonywane asynchronicznie iw takim przypadku getCount() może zwrócić -1, jeśli zostanie wywołane, zanim SQLiteQuery zakończy przygotowywanie danych.

+0

Dziękuję bardzo za te informacje! Nie miałem pojęcia, że ​​db może zamknąć przez wywołanie getCount(). – danh32

Powiązane problemy