在ContentProvider中关闭数据库

Sil*_*owe 73 sqlite android android-contentprovider

本周我一直在学习有关ContentProvider的所有内容,并使用SQLiteOpenHelper类来管理提供程序内部数据库的创建和升级.具体来说,我一直在阅读sdk的samples目录中的NotePad示例.

现在,我可以看到SQLiteOpenHelper有一个close()方法.我知道将空闲数据库打开是不好的做法,可能导致内存泄漏等等(除非这个讨论朝着正确的方向发展).如果我在Activity中使用该类,那么我只需在onDestroy()方法中调用close(),但据我所知,ContentProvider与活动没有相同的生命周期.NotePad的代码似乎永远不会调用close(),所以我想假设它是由SQLiteOpenHelper或其他一些拼图处理的,但我真的很想知道.我真的不太相信示例代码,要么...

问题摘要:我们何时应该关闭提供商中的数据库,如果有的话?

phi*_*ipp 91

根据Dianne Hackborn(Android框架工程师)的说法,不需要在内容提供商中关闭数据库.

内容提供程序是在创建托管进程时创建的,并且只要进程一直保留,所以不需要关闭数据库 - 它将作为内核的一部分关闭,当清理进程的资源时过程被杀死.

谢谢@bigstones指出这一点.

  • 不要让我开始缺少什么:) (16认同)
  • 谢谢.顺便说一句,他们(Android团队)应该在[指南](http://developer.android.com/guide/topics/providers/content-provider-creating.html)中评论这个"简单"的东西,或至少在样本中代码,而不是让编码员在网上搜索它. (5认同)
  • 好的,但是关机功能做了什么.代码是:public void shutdown(){Log.w(TAG,"实现ContentProvider shutdown()以确保所有数据库"+"连接正常关闭"); } (3认同)

Ste*_*ven 21

这个问题有点陈旧但仍然非常重要.请注意,如果您正在以"现代"方式执行操作(例如,使用LoaderManager并创建CursorLoaders以在后台线程中查询ContentProvider),请确保不要在ContentProvider实现中调用db.close().当我尝试访问后台线程中的ContentProvider时,我得到了与CursorLoader/AsyncTaskLoader相关的各种崩溃,这些崩溃是通过删除db.close()调用来解决的.

所以如果你遇到这样的崩溃(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
Run Code Online (Sandbox Code Playgroud)

或者这个(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
Run Code Online (Sandbox Code Playgroud)

或者,如果您在LogCat中看到如下所示的错误消息:

Cursor: invalid statement in fillWindow()
Run Code Online (Sandbox Code Playgroud)

然后检查您的ContentProvider实现,确保您没有过早关闭数据库.根据这一点,无论如何,当进程被终止时,ContentProvider将自动清理,因此您不需要提前关闭其数据库.

也就是说,确保你仍然正确:

  1. 关闭从ContentProvider.query()返回的游标.(CursorLoader/LoaderManager会自动为您执行此操作,但如果您在LoaderManager框架之外进行直接查询,或者您已实现自定义CursorLoader/AsyncTaskLoader子类,则必须确保清理游标正确的.)
  2. 以线程安全的方式实现ContentProvider.(最简单的方法是确保数据库访问方法包装在同步块中.)


ple*_*ock 13

我遵循Mannaz的回答,看到该SQLiteCursor(database, driver, table, query);构造函数已被弃用.然后我找到了getDatabase()方法并用它代替mDatabase指针; 并保持构造函数的后向能力

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


whl*_*hlk 7

如果您希望数据库自动关闭,您可以CursorFactory在打开它时提供:

mContext.openOrCreateDatabase(DB_NAME, SQLiteDatabase.OPEN_READWRITE, new LeaklessCursorFactory());
Run Code Online (Sandbox Code Playgroud)

以下是课程:

public class LeaklessCursorFactory implements CursorFactory {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
        String editTable, SQLiteQuery query) {
        return new LeaklessCursor(db,masterQuery,editTable,query);
    }
}


public class LeaklessCursor extends SQLiteCursor {
    static final String TAG = "LeaklessCursor";
    final SQLiteDatabase mDatabase;

    public LeaklessCursor(SQLiteDatabase database, SQLiteCursorDriver driver, String table, SQLiteQuery query) {
        super(database, driver, table, query);
        mDatabase = database;
    }

    @Override
    public void close() {
        Log.d(TAG, "Closing LeaklessCursor: " + mDatabase.getPath());
        super.close();
        if (mDatabase != null) {
            mDatabase.close();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)