Android 数据库游标生命周期
在过去的三天里,我一直在试图理解并解决这个问题。 我有多个 ListActivities,其中每个都查询同一个数据库。在每个活动中,我分别在 onPause() 和 onResume() 中关闭并重新打开数据库连接。我用光标填充列表适配器,我也关闭并重新打开:
@Override
public void onPause(){
super.onPause();
if(currentCursor != null){
currentCursor.close();
currentCursor = null;
}
if(mDbHelper != null){
mDbHelper.close();
mDbHelper = null;
}
}
@Override
public void onResume(){
super.onResume();
if(mDbHelper == null){
mDbHelper = new DbHelper(this);
mDbHelper.open();
}
if(currentCursor == null){
new LoadNewListTask().execute();
}
}
private class LoadNewListTask extends AsyncTask<String, Void, String> {
ProgressDialog dialog;
@Override
protected void onPreExecute() {
dialog = new ProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
}
@Override
protected String doInBackground(String... arg0) {
try{
currentCursor = mDbHelper.fetchNewRows();
}catch(Exception e){}
return null;
}
@Override
protected void onPostExecute(String arg){
if(dialog != null && dialog.isShowing()){
dialog.dismiss();
}
if(currentCursor != null && currentCursor.getCount()>0){
String[] from = new String[]{DbHelper.KEY_ID, sortingColumn};
int[] to = new int[]{R.id.ai_about_author_btn, R.id.ai_author_name};
MyAlphabetizedAdapter notes = new MyAlphabetizedAdapter(this, R.layout.author_index_item, currentCursor, from, to, sortingColumn);
setListAdapter(notes);
}
}
}
乍一看它工作得很好,但是当我在活动之间快速移动时,我有时会遇到烦人的光标异常,一个说这个:
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):
java.lang.IllegalStateException: attempt to acquire a reference on a
close SQLiteClosable
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:31)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:56)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1118)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1092)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
apt.tutorial.Restaurant.getAll(Restaurant.java:14)
另一个说这个(这种情况在 2.1 设备上经常发生) ):
09-03 11:30:19.713: INFO/dalvikvm(2541): Uncaught exception thrown by finalizer (will be discarded):
09-03 11:30:19.713: INFO/dalvikvm(2541): Ljava/lang/IllegalStateException;: Finalizing cursor android.database.sqlite.SQLiteCursor@45b99828 on null that has not been deactivated or closed
09-03 11:30:19.713: INFO/dalvikvm(2541): at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:596)
09-03 11:30:19.713: INFO/dalvikvm(2541): at dalvik.system.NativeStart.run(Native Method)
更重要的是,我的 ListActivities 之一实现了 AlphabetIndexer,我必须在其中传递光标。在我离开活动并使用后退按钮返回后,发生了非常奇怪的事情。该列表已填充(如预期),但是当我尝试使用快速滚动时,即使我重新创建了它并在 onResume() 上重新填充了列表,我也会在光标上出现空异常。
对我来说,操作系统似乎无法正确处理多个游标,或者我可能在这里错过了一些东西?
感谢您的帮助。
I have been trying to understand and solve this problem for the last three days.
I have multiple ListActivities where each one queries the same database. In every activity I close and reopen DB connection in onPause() and onResume() respectively. I populate list adapters with cursors which I also close and reopen:
@Override
public void onPause(){
super.onPause();
if(currentCursor != null){
currentCursor.close();
currentCursor = null;
}
if(mDbHelper != null){
mDbHelper.close();
mDbHelper = null;
}
}
@Override
public void onResume(){
super.onResume();
if(mDbHelper == null){
mDbHelper = new DbHelper(this);
mDbHelper.open();
}
if(currentCursor == null){
new LoadNewListTask().execute();
}
}
private class LoadNewListTask extends AsyncTask<String, Void, String> {
ProgressDialog dialog;
@Override
protected void onPreExecute() {
dialog = new ProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
}
@Override
protected String doInBackground(String... arg0) {
try{
currentCursor = mDbHelper.fetchNewRows();
}catch(Exception e){}
return null;
}
@Override
protected void onPostExecute(String arg){
if(dialog != null && dialog.isShowing()){
dialog.dismiss();
}
if(currentCursor != null && currentCursor.getCount()>0){
String[] from = new String[]{DbHelper.KEY_ID, sortingColumn};
int[] to = new int[]{R.id.ai_about_author_btn, R.id.ai_author_name};
MyAlphabetizedAdapter notes = new MyAlphabetizedAdapter(this, R.layout.author_index_item, currentCursor, from, to, sortingColumn);
setListAdapter(notes);
}
}
}
On the first glance it works good however when I move fast between activities I sometimes get annoying Cursor Exceptions one saying this:
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):
java.lang.IllegalStateException: attempt to acquire a reference on a
close SQLiteClosable
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:31)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:56)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1118)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1092)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
apt.tutorial.Restaurant.getAll(Restaurant.java:14)
and another one saying this (this happens a LOT on 2.1 devices):
09-03 11:30:19.713: INFO/dalvikvm(2541): Uncaught exception thrown by finalizer (will be discarded):
09-03 11:30:19.713: INFO/dalvikvm(2541): Ljava/lang/IllegalStateException;: Finalizing cursor android.database.sqlite.SQLiteCursor@45b99828 on null that has not been deactivated or closed
09-03 11:30:19.713: INFO/dalvikvm(2541): at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:596)
09-03 11:30:19.713: INFO/dalvikvm(2541): at dalvik.system.NativeStart.run(Native Method)
What is more one of my ListActivities implements AlphabetIndexer where I have to pass the cursor. Very strange thing happens after I leave the activity and come back using back button. The list is populated (as expected) however when I try to use fast scrolling I get a null exception on the cursor even thought I recreated it and repopulated the list onResume().
For me it looks like OS cannot handle multiple cursors properly or maybe I miss something here?
Thanks for your help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一般来说,我在尝试保持多个游标活动的活动方面遇到了问题。特别是当尝试使用托管游标时,我现在尽可能避免使用托管游标(而且它们已被弃用)。现在,我尝试做的是打开游标,用我需要的数据填充对象数组,关闭游标,然后使用 ArrayAdapter 填充 ListView。如果您确实需要保持游标处于活动状态,并且仅当堆栈上的其他活动正在更改数据时才需要,请使用游标管理器,或者更好的是更新的 CursorLoader。执行此操作时,不要关闭光标,因为您的活动将为您管理光标的生命周期。
In general, I've had issues with Activities that try to keep multiple cursors alive. Especially when trying to use a managed cursor which I now try to avoid if possible (plus they're deprecated). Now, what I try to do is open the cursor, populate an array of objects with the data I need, close the cursor and then use an ArrayAdapter to populate the ListView. If you DO need to keep the cursor alive, and you should only need to if other activities on your stack are changing data, use a cursor manager or, better yet, the newer CursorLoader. When doing this, don't close the cursor, as your activity will manage the lifecycle of the cursor for you.
由于您启动异步任务并快速离开该活动,这意味着当任务返回时您将离开该活动,因此
onPause()
已被调用并且光标未关闭。您可以尝试在onPause()
中取消异步任务。Since you start an async task and you leave the activity fast, means that when the task return you will have leave from this activity, thus
onPause()
has been called and cursor is not closed. You could try to cancel the async task in theonPause()
.