java - qué - proveedor de contenido definicion
Problema de fuga en la base de datos del proveedor de contenido de Android (3)
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 que llama. 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 limpia y correcta de devolver un cursor de los resultados de la base de datos?
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.
¿Supongo que está consultando y utilizando los cursores en una actividad? si es así, asegúrese de que está cerrando los cursores llamando a cursor.close();
En cuanto al método, observo que si no está cerrando los cursores en una actividad y luego pasa a otra actividad, recibirá estos mensajes de fugas cuando ejecute otra consulta.
Me parece que es una buena práctica anular el método onDestroy en su actividad y cerrar todos los cursores que contiene.
Uno de mis proveedores de contenido administra varias bases de datos, el mismo esquema con diferentes conjuntos de datos. Para evitar la excepción IllegalStateException 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 que lo referenciara, aunque el SQLiteCursor sí me deja con varias opciones:
1) Deje abierto el objeto SQLiteDatabase y colóquelo en una colección, para no volver a utilizarlo.
2) Deje abierto el objeto SQLiteDatabase y desarrolle un caché que me permita reutilizar el objeto de la base de datos cuando acceda a la misma base de datos.
3) Cerrar la base de datos cuando el cursor está cerrado.
La solución 1 va en contra de mi mejor juicio. Solución uno es solo otra forma de una fuga de recursos; Su salvación es que no trastorna el sistema. Descarté esta elección inmediatamente.
La solución 2 fue mi idea de la mejor solución. Conservó los recursos al mismo tiempo que reducía 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 el momento de escribirlo era. Pasé esta solución por el momento presente y puedo volver a ella más tarde.
La solución 3 es la que decidió irse. Primero, pensé que sería fácil de hacer; en la actividad, todo lo que tenía que hacer era volver a refundir el Cursor devuelto por mi Proveedor de Contenido a 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 de contenedor que impide el acceso directo al objeto Cursor real. El método de los delegados de envoltura llama al cursor al cursor. No hay posibilidad de devolver el cursor a su tipo derivado.
Entonces, en lugar de asignar la responsabilidad de cerrar la base de datos sobre la actividad, tomé una ruta diferente y más fácil. Derivé mi propia clase Cursor extendiendo la clase SQLiteCursor. Agregué dos miembros de datos a mi clase derivada; uno para hacer referencia a la base de datos y el otro era un ID para usar en la depuración. El ctor de la clase tenía la misma firma que el ctor SQLiteCursor con un parámetro adicional agregado al final para establecer el valor de ID. El ctor configuró el miembro de datos de la base de datos para asegurarse de que algo estuviera haciendo referencia a la base de datos si se producía una recolección de basura antes de cerrar el cursor.
Anulé el método close () para que ambos cerraran el cursor [super.close ()] y cerraran la base de datos si la referencia no era nula.
También anulé el método toString () para que el número de ID se agregara a la cadena. Esto me permitió rastrear qué cursores aún estaban 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 que se pasó a cada uno.
Esta solución aún requiere que cada actividad cierre los cursores que se le pasan cuando terminan de usar el cursor. Dado que esta es una buena práctica de programación, no era una preocupación.
No te estás perdiendo nada AFAIK. A Android le 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 observa el código fuente de AlarmProvider
y LauncherProvider
, incluso crean objetos de base de datos por llamada a API (por ejemplo, cada vez que obtienen insert()
, abren un identificador de base de datos grabable que nunca se cierran).