SFLeBrun
Lurker
My application has a ContentProvider that handles the direct SQLiteDatabase access. The activities that query the ContentProvider are returned a Cursor. Even though the activities close the cursor, the application is throwing an IllegalStateException when the ContentProvider exits (or possibly when garbage collection is done) because the activities are not closing the SQLiteDatabase.
The Activity has no direct way to close database. The Cursor returned is an android.content.ContentResolver$CursorWrapperInner type. This type encapsulates the actual SQLiteCursor returned from the ContentProvider.
If the returned Cursor could be cast into its original SQLiteCursor, the SQLiteDatabase used by the Cursor would be accessible and could be closed by the Activity. Unfortunately, the CursorWrapperInner cannot be cast.
This looks like it should be a common problem but I cannot find any references to this issue in any of the forums that I have looked at or by googling. Any help resolving this issue will be appreciated.
Sequence of Events:
1) Activity uses ContentResolver to run a query through a ContentProvider.
2) Content Provider receives the query request through a call to its query() method.
3) Content Provider opens a SQLiteDatabase, performs the query and obtains a SQLiteCursor.
4) Content Provider exits the query() method, returning the SQLiteCursor.
5) Activity receives a CursorWrapperInner object from the ContentResolver.query() call.
6) Activity uses the cursor and invokes the Cursor.close() method.At some later time, either the ContentProvider is deleted or garbage collection occurs. (I am not sure which is the trigger to the Exception)
7) An IllegalStateException is thrown because a SQLiteDatabase remains open and is a leak.
* Closing the SQLiteDatabase in the ContentProvider invalidates the Cursor before the Activity has a chance to use.
* Invoking close() on the Cursor, which is suppose to release all resources held by the Cursor, is not closing the SQLiteDatabase.
* The CursorWrapperInner class prevents the Activity from direct access to the SQLiteCursor which could be used to close the database.
What am I missing?
The following is a snippet from the LogCat:
The Activity has no direct way to close database. The Cursor returned is an android.content.ContentResolver$CursorWrapperInner type. This type encapsulates the actual SQLiteCursor returned from the ContentProvider.
If the returned Cursor could be cast into its original SQLiteCursor, the SQLiteDatabase used by the Cursor would be accessible and could be closed by the Activity. Unfortunately, the CursorWrapperInner cannot be cast.
This looks like it should be a common problem but I cannot find any references to this issue in any of the forums that I have looked at or by googling. Any help resolving this issue will be appreciated.
Sequence of Events:
1) Activity uses ContentResolver to run a query through a ContentProvider.
2) Content Provider receives the query request through a call to its query() method.
3) Content Provider opens a SQLiteDatabase, performs the query and obtains a SQLiteCursor.
4) Content Provider exits the query() method, returning the SQLiteCursor.
5) Activity receives a CursorWrapperInner object from the ContentResolver.query() call.
6) Activity uses the cursor and invokes the Cursor.close() method.At some later time, either the ContentProvider is deleted or garbage collection occurs. (I am not sure which is the trigger to the Exception)
7) An IllegalStateException is thrown because a SQLiteDatabase remains open and is a leak.
* Closing the SQLiteDatabase in the ContentProvider invalidates the Cursor before the Activity has a chance to use.
* Invoking close() on the Cursor, which is suppose to release all resources held by the Cursor, is not closing the SQLiteDatabase.
* The CursorWrapperInner class prevents the Activity from direct access to the SQLiteCursor which could be used to close the database.
What am I missing?
The following is a snippet from the LogCat:
Code:
D/dalvikvm( 722): GC freed 3058 objects / 180664 bytes in 143ms
E/Database( 722): Leak found
E/Database( 722): java.lang.IllegalStateException: /data/data/com.lebruns.android.BookManager/databases/BMMasterCatalog.db SQLiteDatabase created and never closed
E/Database( 722): at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1580)
E/Database( 722): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:638)
E/Database( 722): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:659)
E/Database( 722): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:652)
E/Database( 722): at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:463)
E/Database( 722): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:181)
E/Database( 722): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)
E/Database( 722): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158)
E/Database( 722): at com.lebruns.android.BookManager.BookCaseProvider.QueryMasterCatalog(BookCaseProvider.java:714)
E/Database( 722): at com.lebruns.android.BookManager.BookCaseProvider.query(BookCaseProvider.java:273)
E/Database( 722): at android.content.ContentProvider$Transport.query(ContentProvider.java:129)
E/Database( 722): at android.content.ContentResolver.query(ContentResolver.java:149)
E/Database( 722): at com.lebruns.android.BookManager.Catalog.Refresh(Catalog.java:124)
E/Database( 722): at com.lebruns.android.BookManager.MainActivity.onStart(MainActivity.java:52)
E/Database( 722): at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1205)
E/Database( 722): at android.app.Activity.performStart(Activity.java:3490)
E/Database( 722): at android.app.Activity.performRestart(Activity.java:3518)
E/Database( 722): at android.app.Activity.performResume(Activity.java:3523)
E/Database( 722): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2619)
E/Database( 722): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2647)
E/Database( 722): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1717)
E/Database( 722): at android.os.Handler.dispatchMessage(Handler.java:99)
E/Database( 722): at android.os.Looper.loop(Looper.java:123)
E/Database( 722): at android.app.ActivityThread.main(ActivityThread.java:3948)
E/Database( 722): at java.lang.reflect.Method.invokeNative(Native Method)
E/Database( 722): at java.lang.reflect.Method.invoke(Method.java:521)
E/Database( 722): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
E/Database( 722): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
E/Database( 722): at dalvik.system.NativeStart.main(Native Method)