views:

2454

answers:

3

hi all,

i am getting this exception in database Leak Found

my LOGCAT Shows this:

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)

how can i solve it???

thanks in advance...

A: 

You have to close your database

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

Donal Rafferty
+1  A: 

You need to either close the database object or hold on to the database object so that there is a variable in your Content Provider that is referencing the database object allowing garbage collection to ignore the open database.

The problem with closing the database in the content provider is that the cursor being returned to the activity that requested the query becomes an empty cursor.

So the choice is to either hold on to the open database object for ever (the life time of the Content Provider) or to make sure that the database is closed when the cursor is closed.

I chose the second option and derived a cursor by extending the SQLiteCursor class and implementing the SQLiteDatabase.CursorFactory interface with the following code:

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

This code provides a cursor object that closes the database when the cursor itself is closed, resolving the IllegalStateException during garbage collection. This does place the responsibility of closing the cursor on the activity that requested it. This should not put an extra burden on the activity since closing the cursor when done with it is good practice.

These two classes are nested inside of MyProvider, my content provider class and the data member CursorID is initialized by MyProvider.

SFLeBrun
You could also extend CursorWrapper for the same purpose, no?
sehugg
Actually, you cannot. The reason is that the CursorWrapper class does not provide access to the SQLiteCursor. CursorWrapper is a true wrapper and does not provide you with direct access to the Cursor it contains. In order for my trick to work, you need to access the database object in order to close it. The Cursor interface does not provide this API. Hence, you need to modify the derived Cusror class to do the work.
SFLeBrun
A: 

This code provides a cursor object that closes the database when the cursor itself is closed, resolving the IllegalStateException during garbage collection. This does place the responsibility of closing the cursor on the activity that requested it. This should not put an extra burden on the activity since closing the cursor when done with it is good practice.

These two classes are nested inside of MyProvider, my content provider class and the data member CursorID is initialized by MyProvider.

Then how do you use the closeForReuse method? And if your activity that accesses the provider is from another APK, how would you access your own Cursor class or object? Do you need to add the same class on your Activity that uses the provider and be able to cast the cursor returned by managedQuery?

Mark