2009-09-27 10 views
42

Publiqué this en mayo en el grupo de Google [android-developers]. Nunca recibí respuesta y no pude reproducir el problema hasta que uno de mis alumnos lo hizo la semana pasada. Pensé que lo publicaría aquí y vería si hacía sonar las campanas para cualquiera.Excepción: intento de adquirir una referencia en un cierre SQLiteClosable

En uno de mis ejemplos de código, tengo el siguiente método:

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

} 

Cuando lo ejecuto, esporádicamente, me sale esto:

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) 

Esto no tiene sentido para mí. La base de datos está definitivamente abierta. El SQLiteClosable es el SQLiteQuery creado por SQLiteQueryDriver, y yo ver ninguna evidencia de que hay una agrupación de objetos o algo pasando aquí que podría explicar cómo un "nuevo" SQLiteClosable está ya cerrado. El hecho de que es esporádico (lo que significa que las mismas operaciones de interfaz de usuario a veces activan la excepción, pero no siempre) sugiere algún tipo de agrupación, condición de carrera , o algo así ... pero no estoy seguro de dónde.

¿Pensamientos?

Gracias!

ACTUALIZACIÓN: El código en cuestión es de los tutoriales LunchList de mi libro Android Programming Tutorials. Está un poco extendido y no es muy adecuado para publicar directamente en SO. Puede descargar el código de ese libro desde el enlace de arriba si quiere echarle un vistazo. No recuerdo exactamente en qué edición del tutorial estaba trabajando el alumno en ese momento, aunque estaba en el rango Tutorial 12-Tutorial 16. Tenía muchas esperanzas de encontrar a alguien que había tropezado con este problema antes y que probablemente era el culpable. Estoy bastante seguro de que mi base de datos está abierta. ¡Gracias de nuevo!

+0

¿Hiciste algún progreso en esto? –

+0

Ver la actualización que acabo de agregar a la consulta original. Es una ocurrencia tan rara que no estoy terriblemente preocupado; Solo esperaba que alguien que tenía el problema antes pudiera ver la pregunta. ¡Gracias! – CommonsWare

+0

Sí, sucede muy raramente para mí también. De todos modos, esperaba encontrar al menos un informe de error, pero no lo hice. –

Respuesta

50

Esto me volvió loco por mucho tiempo. La solución que he encontrado es bastante simple: no guarde referencias a los objetos SQLiteDatabase. En su lugar, use un SQLiteOpenHelper y llame al getWritableDatabase() cada vez que lo necesite. A partir de los documentos:

pública sincronizado SQLiteDatabase getWritableDatabase()

crear y/o abrir una base de datos que se utilizará para la lectura y la escritura. Una vez que se haya abierto correctamente, la base de datos se almacena en caché, por lo que puede llamar a este método cada vez que necesite escribir en la base de datos.

La respuesta estuvo ahí todo el tiempo.

+1

Hmmmm ... eso es muy interesante. También parece que puede llamar a 'close()' en SQLiteOpenHelper para cerrar la base de datos. Tendré que experimentar con esto. ¡Muchas gracias! – CommonsWare

+0

¡De nada! Gracias por hacer la pregunta; me ayudó a descubrir la solución. –

+0

No pude conseguirlo. ¿Puedes publicar un ejemplo más elaborado? – Codevalley

1

Tuve el mismo problema durante unos días, y mi solución fue poner el método open() justo antes de la consulta y el método close() después de la operación de la base de datos. Se ve algo como esto.

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

Funciona bien, pero me preocupa el costo de los recursos. No estoy seguro de estar gastando más recursos con toda esta base de datos de apertura y cierre antes de cualquier acción.

1

He estado experimentando un problema similar, a pesar de ya seguir Jarett's advice.En mi caso, el problema ocurre con bastante regularidad en los cambios de orientación. Descubrí que, por alguna razón aún no he llegado al final, mi código genera dos AsyncTasks idénticas, casi simultáneas en un cambio de orientación (a diferencia de una sola cuando la actividad comienza normalmente). Estas tareas realizan la misma consulta de base de datos al mismo tiempo desde diferentes subprocesos.

Esta excepción (u ocasionalmente algunas otras SQLiteException) es el resultado. Por lo tanto, parece que este mensaje puede ser un síntoma de problemas de concurrencia, incluso si no es necesariamente la raíz del problema original publicado aquí.

+0

Cuando la orientación de la pantalla cambia, se crea una actividad completamente nueva. Si está iniciando AsyncTasks en el método onCreate() de la actividad, se crearán dos. –

+0

Si está extendiendo correctamente 'SQLiteOpenHelper' y está cerrando su base de datos en' onDestroy() ', entonces no debería tener ningún problema con las llamadas simultáneas para modificar la base de datos. –

8

SQLiteDatabase se cierra automáticamente bajo ciertas condiciones.

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

getCount() y onMove() de Cursor desencadenan una consulta real usando SQLiteQuery. Una vez que se obtienen todos los datos necesarios, SQLiteQuery reduce el recuento de referencias de la instancia de SQLiteDatabase. Cuando el recuento de referencias llega a 0, la base de datos se cierra.

Tenga en cuenta que la consulta se puede ejecutar de forma asincrónica y, en tal caso, getCount() puede devolver -1 si se invoca antes de que SQLiteQuery termine de preparar los datos.

+0

¡Muchas gracias por esta información! No tenía idea de que el archivo db podría cerrarse llamando a getCount(). – danh32

Cuestiones relacionadas