2009-09-04 13 views
19

Estoy escribiendo un proveedor de contenido para esta aplicación y en mi proveedor de contenido estoy abriendo una conexión de base de datos, ejecutando una consulta y devolviendo el cursor de resultados al programa llamante. Si cierro esta conexión de base de datos en el proveedor, el cursor no tiene resultados. Si lo dejo abierto, obtengo errores de "fuga encontrada" en mi registro DDMS. ¿Que me estoy perdiendo aqui? ¿Cuál es la forma correcta y limpia de devolver un cursor de los resultados de la base de datos?Problema de fuga en la base de datos del proveedor de contenido Android

+2

Parece que no hay necesidad de cerrar la base de datos. La documentación dice que la base de datos está en caché, por lo que solo tendrá una instancia de base de datos abierta para toda la vida útil de la aplicación. http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase() –

Respuesta

5

Está perfectamente bien al dejar la conexión de la base de datos abierta durante todo el tiempo de ejecución de su aplicación, solo tiene que asegurarse de cerrar el cursor una vez que haya terminado con ella.

Supongo que está consultando y usando los cursores en una Actividad? Si es así, asegúrese de cerrar los cursores llamando al método cursor.close();. Noto que si no está cerrando los cursores en una Actividad y luego pasa a otra Actividad, obtendrá estos mensajes de fuga al ejecutar otra consulta.

Creo que es una buena práctica anular el método onDestroy en tu actividad y cerrar todos los cursores en él.

+0

Estoy usando la llamada managedQuery para llamar al proveedor. Tenía la impresión de que manejaba el ciclo de vida del cursor, pero volveré a revisarlo solo para asegurarme. ¿Es seguro ignorar estos errores de fuga? – MattC

+0

Yo también hice las mismas suposiciones sobre el ciclo de vida, comencé a notar que los mensajes de error desaparecían una vez que comencé a cerrar los cursores. En ocasiones recibía Excepciones debido a que los cursores no se habían cerrado al realizar la depuración, cuando sucedió que mató mi sesión de depuración, casi nunca arroja inmediatamente estos errores y excepciones. –

13

No te lo pierdas nada AFAIK. Android falta un onDestroy() (o el equivalente) para ContentProvider. No hay nada en el código fuente en esta área que sugiera que hay algún tipo de onDestroy() que simplemente no aparece en el SDK.

Si nos fijamos en el código fuente de AlarmProvider y LauncherProvider, incluso crear objetos de base de datos en una base por-API-llamada (por ejemplo, cada vez que se insert(), que abrir un identificador de base de datos de escritura que nunca se cierran).

+0

argh eso significa que tengo que vivir con la base de datos abierta :( – Janusz

+0

"incluso crean objetos de base de datos por [...] base de llamada API que nunca cierran", consulte 'SQLiteOpenHelper # getWritableDatabase':" Una vez abierto con éxito, la base de datos está en la memoria caché ". Aunque estoy de acuerdo, esa única instancia debería cerrarse en algún momento ... – TWiStErRob

11

Uno de mis proveedores de contenido administra varias bases de datos, el mismo esquema con diferentes conjuntos de datos. Para evitar que el IllegalStateException ser a través de cuando la recolección de basura descubrió que había una base de datos abierta en mi proveedor de contenido que ya no tenía nada referencia a ella, a pesar de que la SQLiteCursor me dejó con varias opciones:

1) Deja las SQLiteDatabase objeto abierto y colóquelo en una colección, para nunca volver a utilizarlo.

2) Deje el objeto SQLiteDatabase abierto y desarrolle un caché que me permita reutilizar el objeto de la base de datos al acceder a la misma base de datos.

3) Cierre la base de datos cuando el cursor esté cerrado.

Solución 1 va en contra de mi mejor juicio. La solución uno es solo otra forma de fuga de recursos; su única gracia salvadora es que no altera el sistema. Decidí esta opción de inmediato.

Solución 2 fue mi idea de la mejor solución. Conservó recursos al mismo tiempo, reduciendo el tiempo de ejecución al no tener que volver a abrir las conexiones de la base de datos. El inconveniente de esta solución era que tendría que escribir el caché y habría aumentado el tamaño de la aplicación. El tamaño realmente no era un problema, pero era el momento de escribirlo. Pasé esta solución por el momento y puedo volver sobre ella más adelante.

Solución 3 es el que decidió ir con. Primero, pensé que sería simple de hacer; en la actividad, todo lo que tenía que hacer era volver a convertir el Cursor devuelto por mi proveedor de contenido en un SQLiteCursor. Entonces podría invocar su método getDatabase() e invocar close() en la base de datos.

Esto no es posible. Resulta que el cursor devuelto por el proveedor de contenido está dentro de una clase contenedora que impide el acceso directo al objeto real del cursor. El contenedor delega llamadas de método que recibe al Cursor. No hay posibilidad de devolver el cursor a su tipo derivado.

Por lo tanto, en lugar de colocar la responsabilidad de cerrar la base de datos sobre la actividad, tomé una ruta diferente y más fácil. Obtuve mi propia clase de Cursor extendiendo la clase SQLiteCursor. Agregué dos miembros de datos a mi clase derivada; uno para referenciar la base de datos y el otro era una ID para usar en la depuración. El ctor de la clase tiene la misma firma que el cursor SQLiteCursor con un parámetro adicional agregado al final para establecer el valor de ID. El administrador estableció el miembro de datos de la base de datos para asegurarse de que algo hacía referencia a la base de datos si se producía una recolección de elementos no utilizados antes de que se cerrara el cursor.

Anula el método close() para que cierre el cursor [super.close()] y cierre la base de datos si la referencia no fue nula.

También anulé el método toString() para que el número de ID se anexara a la cadena. Esto me permitió rastrear qué cursores seguían abiertos y cuáles se han abierto y cerrado en el archivo de registro.

También agregué un método closeForReuse() para que el proveedor de contenido pudiera reutilizar una base de datos para múltiples consultas sin tener que abrir una nueva conexión de base de datos cada vez.

También creé una clase que implementó la interfaz SQLiteDatabase.CursorFactory. Creó mi nueva clase de cursor derivado y administré el valor de ID pasado a cada uno.

Esta solución aún requiere que cada actividad cierre los cursores que se le pasen cuando hayan terminado con el cursor. Como esta es una buena práctica de programación, no fue una preocupación.

Cuestiones relacionadas