【问题标题】:Sqlite Database LEAK FOUND exception in android?android中的Sqlite数据库泄漏发现异常?
【发布时间】:2010-02-17 11:57:51
【问题描述】:

我在发现数据库泄漏时遇到了这个异常

我的 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)

怎么解决???

提前谢谢...

【问题讨论】:

    标签: android memory-leaks


    【解决方案1】:

    你必须关闭你的数据库

    public DBAdapter open() throws SQLException 
    {
        db = DBHelper.getWritableDatabase();
        return this;
    }
    
    //---closes the database---    
    public void close() 
    {
        DBHelper.close();
    }
    

    http://www.devx.com/wireless/Article/40842/1763?supportItem=4

    【讨论】:

    • 是否也必须关闭cursor
    【解决方案2】:

    您需要关闭数据库对象或保留数据库对象,以便内容提供程序中有一个变量引用数据库对象,从而允许垃圾收集忽略打开的数据库。

    在内容提供程序中关闭数据库的问题是返回给请求查询的活动的游标变成了空游标。

    所以选择要么永远保持打开的数据库对象(内容提供者的生命周期),要么确保在光标关闭时关闭数据库。

    我选择了第二个选项,并通过扩展 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 初始化。

    【讨论】:

    • 你也可以扩展 CursorWrapper 来达到同样的目的,不是吗?
    • 实际上,你不能。原因是 CursorWrapper 类不提供对 SQLiteCursor 的访问。 CursorWrapper 是一个真正的包装器,它不为您提供对其包含的 Cursor 的直接访问。为了使我的技巧起作用,您需要访问数据库对象才能关闭它。 Cursor 接口不提供此 API。因此,您需要修改派生的 Cusror 类来完成这项工作。
    • 你如何使用你的 MyCursor 类?构造函数需要调用 SqliteQueryBuilder.query 时没有的额外参数,该参数返回常规 Cursor?
    【解决方案3】:

    请确保在退出活动之前始终关闭数据库助手。

    db.close();
    

    【讨论】:

    • 我们是否也需要关闭数据库游标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-19
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多