2010-02-17 10 views
5

estoy recibiendo esta excepción en la base de datos de fugas encontradoSqlite Database LEAK FOUND excepción en android?

mi LogCat muestra esto:

02-17 17:20:37.857: INFO/ActivityManager(58): Starting activity: Intent { cmp=com.example.brown/.Bru_Bears_Womens_View (has extras) } 
02-17 17:20:38.477: DEBUG/dalvikvm(434): GC freed 1086 objects/63888 bytes in 119ms 
02-17 17:20:38.556: ERROR/Database(434): Leak found 
02-17 17:20:38.556: ERROR/Database(434): java.lang.IllegalStateException: /data/data/com.example.brown/databases/BRUNEWS_DB_01.db SQLiteDatabase created and never closed 
02-17 17:20:38.556: ERROR/Database(434):  at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1694) 
02-17 17:20:38.556: ERROR/Database(434):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:738) 
02-17 17:20:38.556: ERROR/Database(434):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:760) 
02-17 17:20:38.556: ERROR/Database(434):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:753) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:473) 
02-17 17:20:38.556: ERROR/Database(434):  at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193) 
02-17 17:20:38.556: ERROR/Database(434):  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98) 
02-17 17:20:38.556: ERROR/Database(434):  at com.example.brown.Brown_Splash.onCreate(Brown_Splash.java:52) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.ActivityThread.access$2200(ActivityThread.java:119) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863) 
02-17 17:20:38.556: ERROR/Database(434):  at android.os.Handler.dispatchMessage(Handler.java:99) 
02-17 17:20:38.556: ERROR/Database(434):  at android.os.Looper.loop(Looper.java:123) 
02-17 17:20:38.556: ERROR/Database(434):  at android.app.ActivityThread.main(ActivityThread.java:4363) 
02-17 17:20:38.556: ERROR/Database(434):  at java.lang.reflect.Method.invokeNative(Native Method) 
02-17 17:20:38.556: ERROR/Database(434):  at java.lang.reflect.Method.invoke(Method.java:521) 
02-17 17:20:38.556: ERROR/Database(434):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860) 
02-17 17:20:38.556: ERROR/Database(434):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618) 
02-17 17:20:38.556: ERROR/Database(434):  at dalvik.system.NativeStart.main(Native Method) 

cómo lo puedo resolver ???

gracias de antemano ...

Respuesta

7

Es necesario o bien cerrar el objeto de base de datos o aferrarse al objeto de base de datos para que haya es una variable en su proveedor de contenido que hace referencia al objeto de la base de datos que permite que la recolección de basura ignore la base de datos abierta.

El problema con el cierre de la base de datos en el proveedor de contenido es que el cursor que se devuelve a la actividad que solicitó la consulta se convierte en un cursor vacío.

Por lo tanto, la opción es conservar para siempre el objeto de base de datos abierta (la duración del proveedor de contenido) o asegurarse de que la base de datos se cierre cuando se cierra el cursor.

I eligieron la segunda opción y deriva un cursor extendiendo la clase SQLiteCursor y la implementación de la interfaz SQLiteDatabase.CursorFactory con el siguiente código:

public class MyCursor extends SQLiteCursor 
{ 
    final SQLiteDatabase mDatabase; 
    final int   mID; 


    public MyCursor(SQLiteDatabase  database, 
        SQLiteCursorDriver driver, 
        String    table, 
        SQLiteQuery   query, 
        int     cursorID) 
    { 
     super(database, driver, table, query); 

     mDatabase = database; 
     mID  = cursorID; 
    } 

    /** 
    * Closes the database used to generate the cursor when the 
    * cursor is closed. Hopefully, plugging the GC Leak detected 
    * when using pure SQLiteCursor that are wrapped when returned 
    * to an Activity and therefore unreachable. 
    */ 
    @Override 
    public void close() 
    { 
     super.close(); 
     if (mDatabase != null) 
     { 
      mDatabase.close(); 
     } 
    } 

    /** 
    * Closes cursor without closing database. 
    */ 
    public void closeForReuse() 
    { 
     super.close(); 
    } 

    @Override 
    public String toString() 
    { 
     return super.toString() + ", ID# " + mID; 
    } 

} // end of MyCursor class 


//======================================================================== 
// Nested Class to create the MyCursor for queries 

class MyCursorFactory implements SQLiteDatabase.CursorFactory 
{ 
    /** 
    * Creates and returns a new Cursor of MyCursor type. 
    */ 
    public Cursor newCursor (SQLiteDatabase  database, 
           SQLiteCursorDriver driver, 
           String    editTable, 
           SQLiteQuery   query) 
    { 
     int cursorID = MyProvider.CursorID++; 

     return new MyCursor(database, 
          driver, 
          editTable, 
          query, 
          cursorID); 
    } 

} // end of MyCursorFactory class 

Este código proporciona un objeto cursor que cierra la base de datos cuando el cursor sí mismo está cerrado, resolviendo la IllegalStateException durante la recolección de elementos no utilizados. Esto coloca la responsabilidad de cerrar el cursor en la actividad que lo solicitó. Esto no debe suponer una carga adicional para la actividad, ya que cerrar el cursor una vez hecho esto es una buena práctica.

Estas dos clases están anidadas dentro de MyProvider, mi clase de proveedor de contenido y el miembro de datos CursorID es inicializado por MyProvider.

+0

También podría extender CursorWrapper para el mismo propósito, ¿no? – sehugg

+0

En realidad, no puedes. La razón es que la clase CursorWrapper no proporciona acceso al SQLiteCursor. CursorWrapper es un verdadero contenedor y no le proporciona acceso directo al Cursor que contiene. Para que mi truco funcione, debes acceder al objeto de la base de datos para cerrarlo. La interfaz del Cursor no proporciona esta API. Por lo tanto, debe modificar la clase Cusror derivada para hacer el trabajo. –

+0

¿Cómo se usa su clase MyCursor? El constructor necesita parámetros adicionales que no tiene de una llamada a SqliteQueryBuilder.query que devuelve un Cursor regular? – kenyee

2

Solo asegúrese de cerrar siempre su DB Helper antes de salir de la actividad.

db.close(); 
+0

podemos necesitar cerrar el cursor DB también. –