Android error: java.lang.IllegalStateException: attempt to request an already closed cursor

(Linux / Eclipse Dev for Xoom Tablet works with HoneyComb 3.0.1)

In my application, I use a camera (startIntentForResult ()) to take a picture. After the snapshot is taken, I get the onActivityResult () callback and can load the Bitmap using the Uri that went through the intention of “taking a picture”. At this point, my activity resumes, and I get an error message to reload images in the gallery:

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 

The only cursor logic I use is that after taking the image, I convert the Uri to a file using the following logic

 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; 

Any ideas what I'm doing wrong?

+17
android cursor illegalstateexception android-loadermanager
May 6 '11 at 18:33
source share
6 answers

The managedQuery () call seems to be deprecated in the Honeycomb API.

The Doc for managedQuery () reads:

 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()**. 

I also noticed that I called cursor.close () after a query, which I think is no-no. Found this really useful link . After some reading, I came up with this change that seems to work.

 // 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(); 
+22
May 6 '11 at 18:40
source share

For the record here, how I fixed it in my code (which works on Android 1.6 and above): The problem in my case was that I accidentally closed managed cursors by calling CursorAdapter.changeCursor (). Calling Activity.stopManagingCursor () on the adapter cursor before changing the cursor solved the problem:

 // 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); 
+7
Feb 04 '12 at 11:28
source share

FIX: use context.getContentResolver().query instead of activity.managedQuery .

 Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, PROJECTION, null, null, null); } catch(Exception e) { e.printStackTrace(); } return cursor; 
+5
Sep 19 '11 at 10:17
source share

I created this question here since I could not comment on the last answer (for some reason, comments are disabled). I thought that opening a new thread on this would only complicate the situation.

I get application crashes when I switch from Activity A to Activity B and then return to Activity A. This does not always happen — only occasionally, and it’s hard for me to find EXACTLY where this happens. Everything happens on one device (Nexus S), but I do not think that this is a problem with the device.

I have a few questions regarding @Martin Stine's answer.

  • The documentation says changeCursor(c); : "Change the base cursor to a new cursor. If there is an existing cursor, it will be closed." So why should I stopManagingCursor(currentCursor); - not redundant ?
  • When I use the code suggested by @Martin Stine, I get a null pointer exception. The reason for this is that the first "start" of the application ((SimpleCursorAdapter)getListAdapter()) will evaluate to NULL, because no pointer has yet been created. Of course, I could check if I get null and only then try to stop the cursor control, but finally I decided to place my `stopManagingCursor (currentCursor); in the onPause () method of this activity. I thought that I would surely have a pointer to stop control, and I must do this before I leave the action to others. Problem - I use several cursors (one to fill the text of the EditText field, and the other to represent the list) in my activity, I think not all of them are associated with the ListAdapter cursor -
    • How to find out which one to stop? If I have 3 different kinds of list?
    • Should I close them during onPause() ?
    • How do I get a list of all my open cursors?

So many questions ... Hope someone can help.

When I get to onPause() , I have a pointer to stop the control, but I have not yet decided if this solves the problem, as this error appears sporadically.

Thank you very much!




AFTER SOME RESEARCHES:

I found something interesting that could provide an answer to the "mysterious" side of this problem:

Step A uses two cursors: one to populate the EditText field. Another is to populate a ListView.

When moving from Activity A to Activity B and returning, the + ListView field in Activity A should be filled again. It seems that there will never be a problem with this in the EditText field. I could not find a way to get the current EditText cursor (for example, in Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); ), and the reason tells me that the EditText field will not save it. On the other hand, the ListView will “remember” its cursor from the last time (from Activity A → Activity B). In addition, and this is strange, Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); there will be a different identifier after Activity B → Activity A and all this WITHOUT ever calling

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

I think in some cases when the system needs to free resources, the cursor will be killed, and when Activity B → Activity A, the system will still try to use this old dead cursor, which will lead to an exception. And in other cases, the system will come up with a new cursor that is still alive, and thus an exception will not occur. This may explain why this only appears occasionally. I think this is difficult to debug due to the difference in application speed when starting or debugging the application. Debugging requires more time and, therefore, can give the system time a new cursor or vice versa.

In my understanding, it makes use of

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

As recommended by @Martin Stine a, it should in some cases be redundant in OTHERS: if I return to the method and the system tries to use the dead cursor, I need to create a new cursor and replace it with a ListAdapter, otherwise I get angry application users with a broken application. In another case, when the system finds a new cursor for itself, the lines above are redundant, because they invalidate a good cursor and create a new one.

I assume that to prevent this redundancy, I will need something like this:

 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); 

I would love to hear what you think about it!

+3
Jun 01 '12 at 7:01
source share

Just add the following code to the end of the cursor block.

  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); } 
+2
Jul 09 '13 at 12:16
source share

This problem tormented me for a long time, and I finally came up with a simple solution that works like a charm on all versions of Android. First, do not use startManagingCursor (), as it is obviously buggy and is not recommended in any case. Secondly, close the cursor as soon as possible after you are done with it. I use try and finally to ensure that the cursor closes under any circumstances. If your method should return a cursor, then the calling procedure is responsible for closing it as soon as possible.

I used to make cursors open for the Activity, but since then I have abandoned this transactional approach. Now my application is very stable and does not suffer from an “error in Android: java.lang.IllegalStateException: attempt to request an already closed cursor” when switching actions, even if they access the same database.

  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; } 
0
Oct 23 '12 at 15:15
source share



All Articles