2010-02-17 23 views
5

sto ottenendo questa eccezione nel Leak banca dati TrovatoSqlite Database LEAK FOUND exception in android?

mia Logcat mostra questo:

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) 

come posso risolverlo ???

grazie in anticipo ...

risposta

7

È necessario né chiudere l'oggetto di database o trattenere l'oggetto di database in modo che non ci è una variabile nel provider di contenuti che fa riferimento all'oggetto del database che consente alla garbage collection di ignorare il database aperto.

Il problema con la chiusura del database nel provider di contenuti è che il cursore che viene restituito all'attività che ha richiesto la query diventa un cursore vuoto.

Quindi la scelta è di mantenere l'oggetto di database aperto per sempre (la durata del provider di contenuto) o assicurarsi che il database sia chiuso quando il cursore è chiuso.

ho scelto la seconda opzione e derivati ​​un cursore estendendo la classe SQLiteCursor e implementa l'interfaccia SQLiteDatabase.CursorFactory con il codice seguente:

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 

Questo codice fornisce un oggetto cursore che chiude il database quando il cursore stesso è chiuso, risolvendo l'IllegalStateException durante la garbage collection. Questo pone la responsabilità di chiudere il cursore sull'attività che lo ha richiesto. Questo non dovrebbe comportare un onere aggiuntivo sull'attività poiché chiudere il cursore una volta fatto è una buona pratica.

Queste due classi sono nidificate all'interno di MyProvider, la classe del provider di contenuti e il membro dati CursorID è inizializzato da MyProvider.

+0

Si potrebbe anche estendere CursorWrapper per lo stesso scopo, no? – sehugg

+0

In realtà, non è possibile. Il motivo è che la classe CursorWrapper non fornisce l'accesso a SQLiteCursor. CursorWrapper è un vero wrapper e non ti fornisce accesso diretto al Cursore che contiene. Affinché il mio trucco funzioni, è necessario accedere all'oggetto del database per chiuderlo. L'interfaccia Cursor non fornisce questa API. Quindi, è necessario modificare la classe Cusror derivata per eseguire il lavoro. –

+0

Come si usa la classe MyCursor? Il costruttore ha bisogno di parametri aggiuntivi che non hai da una chiamata a SqliteQueryBuilder.query che restituisce un Cursore regolare? – kenyee

2

Assicurati di chiudere sempre DB Helper prima di uscire dall'attività.

db.close(); 
+0

possiamo anche chiudere il cursore DB. –

Problemi correlati