Android错误:java.lang.IllegalStateException:试图重新查找一个已经closures的游标

环境(运行HoneyComb 3.0.1的Linux / Eclipse Dev for Xoom Tablet)

在我的应用程序中,我使用相机(startIntentForResult())来拍照。 照片后,我得到了onActivityResult()callback,并能够加载一个位图使用Uri通过“拍照”的意图。 在那一点,我的活动被恢复,我得到一个错误,试图重新加载到一个画廊的图像:

FATAL EXCEPTION: main ERROR/AndroidRuntime(4148): java.lang.RuntimeException: Unable to resume activity {...}: java.lang.IllegalStateException: trying to requery an already closed cursor at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2243) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1019) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:126) at android.app.ActivityThread.main(ActivityThread.java:3997) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:491) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.IllegalStateException: trying to requery an already closed cursor at android.app.Activity.performRestart(Activity.java:4337) at android.app.Activity.performResume(Activity.java:4360) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2205) ... 10 more 

我使用的唯一游标逻辑是,在拍摄图像后,我使用以下逻辑将Uri转换为文件

 String [] projection = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION, MediaStore.Images.Media.DATA }; Cursor cursor = activity.managedQuery( uri, projection, // Which columns to return null, // WHERE clause; which rows to return (all rows) null, // WHERE clause selection arguments (none) null); // Order-by clause (ascending by name) int fileColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); if (cursor.moveToFirst()) { return new File(cursor.getString(fileColumnIndex)); } return null; 

任何想法我做错了什么?

Solutions Collecting From Web of "Android错误:java.lang.IllegalStateException:试图重新查找一个已经closures的游标"

看起来像在Honeycomb API中已弃用managedQuery()调用。

对于managedQuery()文档读取:

 This method is deprecated. Use CursorLoader instead. Wrapper around query(android.net.Uri, String[], String, String[], String) that the resulting Cursor to call startManagingCursor(Cursor) so that the activity will manage its lifecycle for you. **If you are targeting HONEYCOMB or later, consider instead using LoaderManager instead, available via getLoaderManager()**. 

另外我注意到,我在查询后调用cursor.close(),我猜是不是。 find这个真正有用的链接 。 经过一番阅读后,我想出了这个似乎起作用的改变。

 // causes problem with the cursor in Honeycomb Cursor cursor = activity.managedQuery( uri, projection, // Which columns to return null, // WHERE clause; which rows to return (all rows) null, // WHERE clause selection arguments (none) null); // Order-by clause (ascending by name) // ------------------------------------------------------------------- // works in Honeycomb String selection = null; String[] selectionArgs = null; String sortOrder = null; CursorLoader cursorLoader = new CursorLoader( activity, uri, projection, selection, selectionArgs, sortOrder); Cursor cursor = cursorLoader.loadInBackground(); 

为了logging,这里是我在我的代码(运行在Android 1.6以上)中解决这个问题:在我的情况下,问题是我无意中通过调用CursorAdapter.changeCursor()closures托pipe的游标。 在更改游标之前在适配器的光标上调用Activity.stopManagingCursor()解决了问题:

 // changeCursor() will close current one for us: we must stop managing it first. Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); // *** adding these lines stopManagingCursor(currentCursor); // *** solved the problem Cursor c = db.fetchItems(selectedDate); startManagingCursor(c); ((SimpleCursorAdapter)getListAdapter()).changeCursor(c); 

FIX:使用context.getContentResolver().query而不是activity.managedQuery

 Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, PROJECTION, null, null, null); } catch(Exception e) { e.printStackTrace(); } return cursor; 

我在这里创build了这个问题,因为我无法对最后一个答案发表评论(由于某些原因,评论被禁用)。 我认为在这个上打开一个新的线程只会使事情复杂化。

当我从活动A转到活动B ,然后返回到活动A时,我得到应用程序崩溃。 这不会一直发生 – 只是有时候,我很难find确切发生的地方。 所有发生在同一设备(Nexus S),但我不认为这是一个设备问题。

关于@Martin Stine的回答,我有几个问题。

  • 在文档中提到changeCursor(c); :“将底层游标更改为一个新的游标,如果有一个现有的游标将被closures”。 那么为什么我必须stopManagingCursor(currentCursor);不是多余的
  • 当我使用@Martin Stine提供的代码时,我得到一个空指针exception。 原因是应用程序的第一个“运行” ((SimpleCursorAdapter)getListAdapter())将计算为NULL,因为尚未创build游标。 当然,我可以检查,如果我不得到一个空,只有然后尝试停止pipe理光标,但最后我决定放置我的`stopManagingCursor(currentCursor); 在此活动的onPause()方法中。 我想这样我肯定会有一个游标停止pipe理,我应该做的就在我离开活动到一个不同的。 问题 – 我在我的活动中使用几个游标(一个填充EditText字段的文本,另一个用于列表视图)我猜并不是所有的游标都与ListAdapter游标有关 –
    • 我怎么知道哪一个停止pipe理? 如果我有3个不同的列表视图?
    • 我应该在onPause()期间全部closures吗?
    • 我如何获得所有打开的游标列表?

这么多的问题…希望任何人都可以帮忙。

当我到onPause()我有一个游标停止pipe理,但我还没有确定这是否解决了这个问题,因为这个错误偶尔出现。

非常感谢!


调查后:

我发现了一些有趣的东西,可以给出这个问题的“神秘”一面的答案:

活动A使用两个游标:一个填充EditText字段。 另一个是填充一个ListView。

当从活动A移动到活动B并返回时,活动A中的字段+ ListView必须重新填充。 看起来EditText字段永远不会有问题。 我无法find一种方法来获取EditText字段的当前游标(如Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); )和原因告诉我,EditText字段不会保留它。 另一方面,ListView将从上次(从活动A→活动B之前)“记住”它的光标。 另外,这也是奇怪的事情, Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); 在活动B – >活动A之后将有一个不同的ID,这一切都不需要我打电话

 Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); stopManagingCursor(currentCursor); 

我猜在某些情况下,当系统需要释放资源时,游标将被杀死,当Activity B – > Activity A时,系统仍然会尝试使用这个旧的死游标,这将导致Exception。 而在其他情况下,系统会产生一个新的游标,它仍然存在,因此不会发生exception。 这也许可以解释为什么有时只会显示出来。 我想这是很难debugging,因为运行或debugging应用程序时的应用程序速度的差异。 debugging时,需要更多的时间,因此可能会给系统一个新的光标,反之亦然。

在我的理解这使得使用

 Cursor currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor(); stopManagingCursor(currentCursor); 

正如@Martin所推荐的那样,在某些情况下必须使用它 ,而在其他情况下则是多余的:如果我返回方法并且系统试图使用死光标,则必须创build一个新的光标并将其replace为ListAdapter,否则我会生气应用程序用户崩溃的应用程序。 在另一种情况下,系统会发现自己是一个新的游标 – 上面的行是冗余的,因为它们使一个好的游标失效并创build一个新的游标。

我想为了防止这种冗余,我需要这样的东西:

 ListAdapter currentListAdapter = getListAdapter(); Cursor currentCursor = null; Cursor c = null; //prevent Exception in case the ListAdapter doesn't exist yet if(currentListAdapter != null) { currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor(); //make sure cursor is really dead to prevent redundancy if(currentCursor != null) { stopManagingCursor(currentCursor); c = db.fetchItems(selectedDate); ((SimpleCursorAdapter)getListAdapter()).changeCursor(c); } else { c = db.fetchItems(selectedDate); } } else { c = db.fetchItems(selectedDate); } startManagingCursor(c); 

我很想听听你对此的看法!

只要在光标块的末尾添加下面的代码即可。

  try { Cursor c = db.displayName(number); startManagingCursor(c); if (!c.moveToFirst()) { if (logname == null) logname = "Unknown"; System.out.println("Null " + logname); } else { logname = c.getString(c .getColumnIndex(DataBaseHandler.KEY_NAME)); logdp = c.getBlob(c .getColumnIndex(DataBaseHandler.KEY_IMAGE)); // tvphoneno_oncall.setText(logname); System.out.println("Move name " + logname); System.out.println("Move number " + number); System.out.println("Move dp " + logdp); } stopManagingCursor(c); } 

这个问题困扰了我很长一段时间,我终于想出了一个简单的解决scheme,就像所有版本的Android的魅力。 首先,不要使用startManagingCursor(),因为它在任何情况下显然都是错误的。 其次,在完成之后尽快closures光标。 我使用try,最后确保游标在任何情况下都closures。 如果你的方法必须返回一个Cursor,那么调用例程负责尽快closures它。

我曾经在一个Activity的生命周期中让游标保持开放,但是我已经放弃了这个交易方法。 现在我的应用程序非常稳定,并且在切换活动时即使访问相同的数据库也不会遇到“Android错误:java.lang.IllegalStateException:尝试重新查找已经closures的游标”。

  static public Boolean musicReferencedByOtherFlash(NotesDB db, long rowIdImage) { Cursor dt = null; try { dt = db.getNotesWithMusic(rowIdImage); if ( (dt != null) && (dt.getCount() > 1)) return true; } finally { if (dt != null) dt.close(); } return false; }