本周我一直在学习有关ContentProvider的所有内容,并使用SQLiteOpenHelper类来管理提供程序内部数据库的创建和升级。具体来说,我一直在阅读sdk的samples目录中的NotePad示例。
现在,我可以看到SQLiteOpenHelper有一个close()方法。我知道将空闲数据库打开是不好的做法,可能导致内存泄漏等等(除非this讨论朝着正确的方向前进)。如果我在Activity中使用该类,那么我只需在onDestroy()方法中调用close(),但据我所知,ContentProvider与活动没有相同的生命周期。 NotePad的代码似乎永远不会调用close(),所以我想假设它是由SQLiteOpenHelper或其他一些拼图处理的,但我真的很想知道。我不太相信示例代码,或者......
问题摘要:我们应该何时关闭提供商中的数据库,如果有的话?
答案 0 :(得分:90)
According to Dianne Hackborn(Android框架工程师)无需在内容提供商中关闭数据库。
在创建托管进程时创建内容提供程序,并且 只要这个过程一直存在,所以没有必要 关闭数据库 - 它将作为内核的一部分关闭 在进程被杀死时清理进程的资源。
感谢@bigstones指出这一点。
答案 1 :(得分: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
或者这个(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
或者,如果您在LogCat中看到如下所示的错误消息:
Cursor: invalid statement in fillWindow()
然后检查您的ContentProvider实现,确保您没有过早关闭数据库。根据{{3}},无论如何都会在进程被终止时自动清理ContentProvider,因此您不需要提前关闭其数据库。
那就是说,确保你仍然正确:
答案 2 :(得分: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);
}
}
答案 3 :(得分:7)
如果您希望数据库自动关闭,则可以在打开时提供CursorFactory
:
mContext.openOrCreateDatabase(DB_NAME, SQLiteDatabase.OPEN_READWRITE, new LeaklessCursorFactory());
以下是课程:
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();
}
}
}
答案 4 :(得分:1)
完成后关闭它,最好是在最后一个块中关闭它,这样你就可以确保它发生了。我知道这听起来有点陈旧而且袖手旁观,但它确实是我所知道的唯一答案。如果您打开数据库并执行操作,请在完成该操作后将其关闭,除非您知道将再次需要它(在这种情况下,一旦不再需要它就一定要关闭它)。
答案 5 :(得分:0)
如果您在活动中使用内容提供商,那么我认为您不必维护内容提供商的连接。您可以使用startManagingCursor管理返回的游标对象。在onPause方法的活动中,您可以释放内容提供者。 (您可以在onResume中重新加载它)。假设活动生命周期通常是有限的,这就足够了。 (根据我的意见;))