environment (Linux/Eclipse Dev for Xoom Tablet running HoneyComb 3.0.1)
In my app I'm using the camera (startIntentForResult()) to take a picture. After the picture is taken I get the onActivityResult() callback and am able to load a Bitmap using a Uri passed via the "take picture" intent. At that point my activity is resumed and I get an error trying to reload the images into a 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'm using is that after the image is taken 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?
For the record, here's how I fixed this in my code (which runs on Android 1.6 and up): The problem in my case was that I was inadvertently closing managed cursors by calling CursorAdapter.changeCursor(). Calling Activity.stopManagingCursor() on the adapter's cursor before changing the cursor solved the problem:
Just add the following code at the end of the cursor block..
Looks like the managedQuery() call is deprecated in the Honeycomb API.
Doc for managedQuery() reads:
Also I noticed that I was calling cursor.close() after the query which I guess is a no-no. Found this really helpful link as well. After some reading I came up with this change that seems to work.
This problem plagued 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, don't use startManagingCursor() since it is evidently buggy and deprecated in any case. Secondly, close a Cursor as soon as possible after you are done with it. I use try and finally to insure that the Cursor gets closed under all circumstances. If your method must return a Cursor then the calling routine is responsible for closing it as soon as possible.
I used to leave cursors open for the lifetime of an Activity but I have since abandoned that for this transactional approach. Now my app is very stable and does not suffer from "Android error: java.lang.IllegalStateException: trying to requery an already closed cursor" when switching Activities even though they access the same database.
FIX: use
context.getContentResolver().query
instead ofactivity.managedQuery
.I've created this question here as I could not comment on the last answer (comments disabled for some reason). I thought opening a new thread on this will only complicate things.
I get application crashes when I go from Activity A to Activity B and then going back to Activity A. This doesn't happen all the time - only sometimes and I am having hard time finding EXACTLY where this happens. All happens on the same device (Nexus S) but I don't believe this is a device issue.
I have few questions regarding @Martin Stine's answer.
changeCursor(c);
: "Change the underlying cursor to a new cursor. If there is an existing cursor it will be closed". So why do I have tostopManagingCursor(currentCursor);
- isn't that redundant?((SimpleCursorAdapter)getListAdapter())
will evaluate to NULL because no cursor was yet to be created. Sure I could check if I DON'T get a null and only then try stopping to manage the cursor but finally I decided to place my `stopManagingCursor(currentCursor); in the onPause() method of this activity. I thought this way I will for sure have a cursor to stop managing and I should do it just before I leave the Activity to a different one. The issue - I am using several cursors (one to fill up a text of an EditText field and the other for a list view) in my activity I guess not all of them are related to a ListAdapter cursor -onPause()
?So many questions... Hope anyone could help.
When I get to
onPause()
I do have a cursor to stop managing but I am yet to determine if this solves the problem as this error shows up sporadically.Many thanks!
AFTER SOME INVESTIGATION:
I found something interesting that might give the answer to the "mysterious" side of this issue:
Activity A uses two cursors: one to fill up an EditText field. The other is to populate a ListView.
When moving from Activity A to Activity B and coming back, the field + ListView in Activity A must be filled up again. It seems like the EditText field will never have an issue with that. I couldn't find a way to get the EditText field's current cursor (like in
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
) and reason tells me that the EditText field will not keep it. On the other hand the ListView will "remember" its cursor from last time (from before Activity A -> Activity B). In addition, and this is the strange thing, theCursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
will have a different ID after Activity B -> Activity A and all this WITHOUT me ever callingI guess 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 result in an Exception. And in other cases the system will come up with a new cursor that is still alive and thus no Exception will occur. This might explain why this shows up only sometimes. I guess this is hard to debug due to the difference of application speed when either running OR debugging the application. When debugging, it takes more time and thus might give the system time to come up with a new cursor or vice versa.
In my understanding this makes the usage of
As recommended by @Martin Stine a must in SOME cases and redundant in OTHERS: if I return to the method and the system is trying to use a dead cursor, one must create a new cursor and replace it in the ListAdapter, otherwise I get angry app users with a crashed app. In another case when the system will find itself a new cursor - the lines above are redundant as they invalidate a good cursor and create a new one.
I guess in order to prevent this redundancy I would need something like this:
I would love to hear what you think about this!