2009-09-27 15 views
42

Ho inviato this a maggio sul [Google-sviluppatori] Google Group. Non ho mai sentito indietro e non sono stato in grado di riprodurre il problema fino a quando uno dei miei studenti non ha fatto la scorsa settimana. Ho pensato di pubblicarlo qui e vedere se suonava qualche campanello per chiunque.Eccezione: tentativo di acquisire un riferimento su una chiusura SQLiteClosable

In uno dei miei esempi di codice, ho il seguente metodo:

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

} 

quando l'eseguo, sporadicamente, ottengo questo:

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) 

Questo non ha senso per me. Il database è definitivamente aperto. Il SQLiteClosable è il SQLiteQuery creato da SQLiteQueryDriver, e io vedono alcuna prova che ci sia un pool di oggetti o di qualcosa da fare qui che potrebbe spiegare come un "nuovo" SQLiteClosable è già chiuso. Il è sporadico (il che significa che le stesse operazioni dell'interfaccia utente a volte attivano l'eccezione, ma non sempre) suggerisce una sorta di piscina, la condizione di gara o qualcosa del genere ... ma non sono sicuro di dove.

Pensieri?

Grazie!

UPDATE: il codice in questione proviene dai tutorial LunchList del mio libro Android Programming Tutorials. È un po 'diffuso e non è particolarmente adatto per pubblicare direttamente in SO. Puoi scaricare il codice per quel libro dal link sopra se vuoi dare un'occhiata a questo libro. Non ricordo esattamente quale edizione del tutorial su cui lo studente stava lavorando al momento, sebbene fosse nella gamma di Tutorial 12-Tutorial 16. Speravo in gran parte di imbattersi in qualcuno che aveva già inciampato su questo problema e aveva probabilmente un colpevole. Sono abbastanza sicuro che il mio database sia aperto. Grazie ancora!

+0

Hai fatto progressi in questo? –

+0

Vedere l'aggiornamento che ho appena aggiunto alla query originale. È un evento così raro che non sono terribilmente preoccupato; Speravo solo che qualcuno che avesse avuto il problema prima avrebbe visto la domanda. Grazie! – CommonsWare

+0

Sì, capita molto raramente anche per me. Comunque mi sarei aspettato di trovare almeno un bug report per questo, ma non l'ho fatto. –

risposta

50

Questo mi ha fatto impazzire per il tempo più lungo. La soluzione che ho trovato è abbastanza semplice: non conservare i riferimenti agli oggetti SQLiteDatabase. Invece, usa un SQLiteOpenHelper e chiama lo getWritableDatabase() ogni volta che ne hai bisogno. Dalla documentazione:

pubblico sincronizzato SQLiteDatabase getWritableDatabase()

creare e/o aprire un database che verrà utilizzato per la lettura e la scrittura. Una volta aperto correttamente, il database viene memorizzato nella cache, quindi è possibile chiamare questo metodo ogni volta che è necessario scrivere nel database.

La risposta era proprio lì per tutto il tempo.

+1

Hmmmm ... è molto interessante. Sembra anche che tu possa chiamare 'close()' su SQLiteOpenHelper per chiudere il database. Dovrò sperimentare con questo. Grazie molto! – CommonsWare

+0

Sei il benvenuto! Grazie per aver chiesto la domanda; mi ha aiutato a capire la soluzione. –

+0

Impossibile capirlo. Puoi pubblicare un esempio più elaborato? – Codevalley

1

Ho avuto lo stesso problema per alcuni giorni, e la mia soluzione era di mettere il metodo open() appena prima della mia query e il metodo close() dopo l'operazione del database. Sembra qualcosa del genere.

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

Funziona bene, ma sono preoccupato per il costo delle risorse. Non sono sicuro che spenderò più risorse con tutti questi database di apertura e chiusura prima di qualsiasi azione.

1

Ho riscontrato un problema simile, nonostante stia già seguendo Jarett's advice.Nel mio caso il problema sta accadendo abbastanza regolarmente sui cambiamenti di orientamento. Ho scoperto che, per qualche ragione, non sono ancora arrivato alla fine, il mio codice genera due AsyncTasks identici, più o meno simultanei su un cambiamento di orientamento (al contrario di uno solo quando l'attività inizia normalmente). Queste attività eseguono la stessa query di database contemporaneamente da diversi thread.

Questa eccezione (o occasionalmente qualche altra SQLiteException) è il risultato. Quindi sembra che questo messaggio possa essere un sintomo di problemi di concorrenza, anche se non è necessariamente la radice del problema originale pubblicato qui.

+0

Quando l'orientamento dello schermo cambia, viene creata un'intera nuova attività. Se stai iniziando i tuoi AsyncTasks nel metodo onCreate() di Activity, ne verranno creati due. –

+0

Se estendi correttamente 'SQLiteOpenHelper' e chiudi il tuo database in' onDestroy() ', allora non dovresti avere problemi con le chiamate simultanee per modificare il database. –

8

SQLiteDatabase viene chiuso automaticamente in alcune condizioni.

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

getCount() e onMove() i metodi di cursore innescano una query reale utilizzando SQLiteQuery. Dopo aver ottenuto tutti i dati richiesti, SQLiteQuery riduce il conteggio dei riferimenti dell'istanza SQLiteDatabase. Quando il conteggio dei riferimenti raggiunge 0, il database viene chiuso.

Si noti che la query può essere eseguita in modo asincrono e in tal caso, getCount() potrebbe restituire -1 se viene chiamato prima che SQLiteQuery completi i dati di preparazione.

+0

Grazie mille per queste informazioni! Non avevo idea che il db potesse chiudere chiamando getCount(). – danh32

Problemi correlati