2010-02-17 18 views
5

我得到这个例外在数据库泄漏找到Android中的Sqlite数据库LEAK FOUND异常?

我的logcat表明这一点:

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) 

我怎样才能解决呢???

在此先感谢...

回答

7

您需要可以关闭数据库对象或保持到数据库对象,以便有是Content Provider中的一个变量,它引用允许垃圾收集忽略打开的数据库的数据库对象。

关闭内容提供程序中的数据库的问题是,返回到请求查询的活动的游标将成为空游标。

因此,选择要么永远保持开放数据库对象(内容提供者的生命期),要么保证在光标关闭时关闭数据库。

我选择了第二选项,并通过扩展SQLiteCursor类和用下面的代码实现SQLiteDatabase.CursorFactory接口导出的光标:

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 

此代码提供关闭数据库光标对象当光标本身已关闭,在垃圾回收期间解决IllegalStateException。这确实将光标关闭在请求它的活动上。这不应该给活动带来额外的负担,因为在完成这个活动后关闭游标是一种很好的做法。

这两个类嵌套在MyProvider中,我的内容提供者类和数据成员CursorID由MyProvider初始化。

+0

你也可以为相同的目的扩展CursorWrapper,不是吗? – sehugg

+0

其实你不行。原因是CursorWrapper类不提供对SQLiteCursor的访问。 CursorWrapper是一个真正的包装器,并不能直接访问它包含的Cursor。为了让我的技巧发挥作用,您需要访问数据库对象才能关闭它。 Cursor接口不提供此API。因此,您需要修改派生的Cusror类来完成这项工作。 –

+0

你如何使用MyCursor类?构造函数需要额外的参数,而这些参数对SqliteQueryBuilder.query的调用并不会返回正常的Cursor? – kenyee

2

只要确保在退出活动之前始终关闭DB助手。

db.close(); 
+0

我们是否也需要关闭DB游标。 –