2010-02-17 7 views
5

otrzymuję ten wyjątek w Leak bazy ZnalezionoSqlite Database LEAK FOUND wyjątek w Androidzie?

mój logcat pokazuje to:

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) 

jak mogę go rozwiązać ???

z góry dzięki ...

Odpowiedz

7

Trzeba albo zamknąć obiektu bazy danych lub trzymać obiektu bazy danych, tak aby nie jest zmienną w dostawcy treści, która odwołuje się do obiektu bazy danych, pozwalając, aby garbage collect ignorował otwartą bazę danych.

Problem z zamknięciem bazy danych w dostawcy zawartości polega na tym, że kursor zwracany do działania, które zażądało zapytania, staje się pustym kursorem.

Tak więc wybór polega na przytrzymaniu obiektu otwartej bazy danych na zawsze (czas życia dostawcy treści) lub upewnieniu się, że baza danych jest zamknięta po zamknięciu kursora.

że wybrano opcję drugą i pochodzących kursorem przez rozszerzenie klasy SQLiteCursor i realizacji SQLiteDatabase.CursorFactory łączenia z następnym kodem:

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 

Kod zawiera obiekt kursor, który zamyka się w bazie danych, gdy kursor sam jest zamknięty, rozwiązując IllegalStateException podczas zbierania śmieci. Obowiązek ten polega na zamknięciu kursora na aktywności, która o to poprosiła. Nie powinno to nakładać dodatkowego obciążenia na działanie, ponieważ zamknięcie kursora, gdy zostanie zrobione, jest dobrą praktyką.

Te dwie klasy są zagnieżdżone wewnątrz MyProvider, moja klasa dostawcy treści i element danych CursorID jest inicjowany przez MyProvider.

+0

Można również rozszerzyć CursorWrapper w tym samym celu, nie? – sehugg

+0

Właściwie nie możesz. Powodem jest to, że klasa CursorWrapper nie zapewnia dostępu do SQLiteCursor. CursorWrapper jest prawdziwym opakowaniem i nie zapewnia bezpośredniego dostępu do zawartego w nim kursora. Aby moja sztuczka działała, musisz uzyskać dostęp do obiektu bazy danych, aby go zamknąć. Interfejs Cursor nie zapewnia tego interfejsu API. W związku z tym należy zmodyfikować pochodną klasę Cusror w celu wykonania pracy. –

+0

Jak korzystać z klasy MyCursor? Konstruktor potrzebuje dodatkowych parametrów, których nie masz w wywołaniu SqliteQueryBuilder.query, które zwraca zwykły kursor? – kenyee

2

Upewnij się, że zawsze zamkniesz pomocnika DB przed zamknięciem aktywności.

db.close(); 
+0

możemy również zamknąć kursor DB. –