2010-02-17 7 views
5

Ich erhalte diese Ausnahme in der Datenbank Leak diese gefundenSqlite Database LEAK FOUND Ausnahme in Android?

meine Logcat Shows:

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) 

, wie ich es lösen kann ???

Dank im Voraus ...

Antwort

7

Sie müssen das Datenbankobjekt oder halten Sie sich an das Datenbankobjekt schließen entweder schließen, so dass es ist eine Variable in Ihrem Inhaltsanbieter, die auf das Datenbankobjekt verweist, sodass die Garbage Collection die geöffnete Datenbank ignorieren kann.

Das Problem beim Schließen der Datenbank im Inhaltsanbieter besteht darin, dass der Cursor, der an die Aktivität zurückgegeben wird, die die Abfrage angefordert hat, ein leerer Cursor wird.

Die Wahl besteht also darin, das geöffnete Datenbankobjekt für immer zu belassen (die Lebensdauer des Inhaltsanbieters) oder sicherzustellen, dass die Datenbank geschlossen wird, wenn der Cursor geschlossen wird.

wählte ich die zweite Option und einen Cursor abgeleitet, indem die Klasse SQLiteCursor erstreckt und den SQLiteDatabase.CursorFactory Schnittstelle mit dem folgenden Code Implementieren:

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 

Dieser Code ein Cursor Objekt liefert, die die Datenbank schließt, wenn die Cursor selbst ist geschlossen und löst die IllegalStateException während der Garbage Collection auf. Dies bedeutet, dass der Cursor auf die Aktivität, die sie angefordert hat, geschlossen wird. Dies sollte die Aktivität nicht zusätzlich belasten, da das Schließen des Cursors, wenn Sie damit fertig sind, eine gute Übung ist.

Diese beiden Klassen sind innerhalb von MyProvider verschachtelt, meine Content-Provider-Klasse und die Datenelement-CursorID wird von MyProvider initialisiert.

+0

Sie könnten CursorWrapper auch für den gleichen Zweck erweitern, nein? – sehugg

+0

Eigentlich können Sie nicht. Der Grund dafür ist, dass die CursorWrapper-Klasse keinen Zugriff auf den SQLiteCursor bietet. CursorWrapper ist ein echter Wrapper und bietet Ihnen keinen direkten Zugriff auf den enthaltenen Cursor. Damit mein Trick funktioniert, müssen Sie auf das Datenbankobjekt zugreifen, um es zu schließen. Die Cursorschnittstelle stellt diese API nicht bereit. Daher müssen Sie die abgeleitete Cusror-Klasse ändern, um die Arbeit zu erledigen. –

+0

Wie verwenden Sie Ihre MyCursor-Klasse? Der Konstruktor benötigt zusätzliche Parameter, die Sie nicht von einem Aufruf von SqliteQueryBuilder.query haben, der einen normalen Cursor zurückgibt? – kenyee

2

Stellen Sie sicher, dass Sie Ihren DB Helper immer schließen, bevor Sie die Aktivität beenden.

db.close(); 
+0

können wir den DB-Cursor auch schließen. –