Closing the database in a ContentProvider Closing the database in a ContentProvider android android

Closing the database in a ContentProvider


According to Dianne Hackborn (Android framework engineer) there is no need to close the database in a content provider.

A content provider is created when its hosting process is created, and remains around for as long as the process does, so there is no need to close the database -- it will get closed as part of the kernel cleaning up the process's resources when the process is killed.

Thanks @bigstones for pointing this out.


This question is a bit old but is still quite relevant. Note that if you're doing things the 'modern' way (e.g. using LoaderManager and creating CursorLoaders to query a ContentProvider in a background thread), make sure that you do NOT call db.close() in your ContentProvider implementation. I was getting all sorts of crashes relating to CursorLoader/AsyncTaskLoader when it tried to access the ContentProvider in a background thread, which were resolved by removing the db.close() calls.

So if you're running into crashes that look like this (Jelly Bean 4.1.1):

Caused by: java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.    at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)    at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:677)    at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)    at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)    at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)    at android.content.ContentResolver.query(ContentResolver.java:388)    at android.content.ContentResolver.query(ContentResolver.java:313)    at com.hindsightlabs.paprika.loaders.GroceryListLoader.loadInBackground(GroceryListLoader.java:147)    at com.hindsightlabs.paprika.loaders.GroceryListLoader.loadInBackground(GroceryListLoader.java:1)    at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:240)    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:51)    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:40)    at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:123)    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)    ... 4 more

Or this (ICS 4.0.4):

Caused by: java.lang.IllegalStateException: database /data/data/com.hindsightlabs.paprika/databases/Paprika.db (conn# 0) already closed    at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2215)    at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:436)    at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:422)    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:79)    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)    at android.content.ContentResolver.query(ContentResolver.java:318)    at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:49)    at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:35)    at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:240)    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:51)    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:40)    at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:123)    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)    ... 4 more

Or if you're seeing error messages in LogCat that look like this:

Cursor: invalid statement in fillWindow()

Then check your ContentProvider implementation and make sure you're not closing the database prematurely. According to this, the ContentProvider will get cleaned up automatically when the process is killed anyway, so you don't need to close its database ahead of time.

That said, make sure you are still correctly:

  1. Closing your Cursors that are returned from ContentProvider.query(). (CursorLoader/LoaderManager does this automatically for you, but if you're doing direct queries outside of the LoaderManager framework, or you've implemented a custom CursorLoader/AsyncTaskLoader subclass, you'll have to make sure you're cleaning up your cursors properly.)
  2. Implementing your ContentProvider in a thread-safe way. (The easiest way to do this is to make sure your database access methods are wrapped in a synchronized block.)


Ive follow Mannaz's answer and saw that SQLiteCursor(database, driver, table, query); constructor is deprecated. Then I found getDatabase() method and used it instead of mDatabase pointer; and kept constructor for backward capability

public class MyOpenHelper extends SQLiteOpenHelper {    public static final String TAG = "MyOpenHelper";    public static final String DB_NAME = "myopenhelper.db";    public static final int DB_VESRION = 1;    public MyOpenHelper(Context context) {        super(context, DB_NAME, new LeaklessCursorFactory(), DB_VESRION);    }    //...}public class LeaklessCursor extends SQLiteCursor {    static final String TAG = "LeaklessCursor";    public LeaklessCursor(SQLiteDatabase db, SQLiteCursorDriver driver,            String editTable, SQLiteQuery query) {        super(db, driver, editTable, query);    }    @Override    public void close() {        final SQLiteDatabase db = getDatabase();        super.close();        if (db != null) {            Log.d(TAG, "Closing LeaklessCursor: " + db.getPath());            db.close();        }    }}public class LeaklessCursorFactory implements CursorFactory {    @Override    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,        String editTable, SQLiteQuery query) {        return new LeaklessCursor(db,masterQuery,editTable,query);    }}