环境(运行HoneyComb 3.0.1的Xoom平板电脑的Linux / Eclipse Dev)
在我的应用程序中,我正在使用相机(startIntentForResult())来拍照。拍摄照片后,我得到了onActivityResult()回调,并且能够使用通过“拍照”意图传递的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;
任何想法我做错了什么?
答案 0 :(得分:23)
看起来在Honeycomb API中不推荐使用managedQuery()调用。
doc for 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(),我猜这是禁止的。找到了这个really helpful link。经过一番阅读后,我想出了这个似乎有效的改变。
// 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();
答案 1 :(得分:7)
为了记录,这是我在我的代码中运行的方法(在Android 1.6及更高版本上运行):我的问题是我无意中通过调用CursorAdapter.changeCursor()来关闭托管游标。在更改光标之前在适配器的光标上调用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);
答案 2 :(得分:5)
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;
答案 3 :(得分:3)
我在这里创建了这个问题,因为我无法对最后的答案发表评论(由于某种原因,评论被禁用)。我认为在这上面开一个新线程只会使事情复杂化。
当我从活动A 转到活动B 然后返回活动A 时,我的应用程序崩溃了。这不会一直发生 - 只是有时候,我很难找到确切发生的地方。所有这些都发生在同一台设备上(Nexus S),但我不认为这是设备问题。
关于@Martin Stine的回答我几乎没有问题。
changeCursor(c);
:“将基础光标更改为新光标。如果有现有光标,它将被关闭”。那么为什么我必须stopManagingCursor(currentCursor);
- 不是多余的? ((SimpleCursorAdapter)getListAdapter())
的第一个“运行”中将评估为NULL,因为尚未创建游标。当然,我可以检查是否我没有得到null,然后尝试停止管理光标但最后我决定放置我的`stopManagingCursor(currentCursor);在此活动的onPause()方法中。我想这样我肯定会有一个光标停止管理,我应该在将Activity留给另一个之前做。问题 - 我在我的活动中使用了几个游标(一个用于填充EditText字段的文本,另一个用于列表视图)我猜不是所有游标都与ListAdapter游标相关 -
onPause()
期间关闭所有这些吗? 这么多问题......希望有人能提供帮助。
当我到达onPause()
时,我确实有一个光标停止管理,但我还没有确定这是否解决了这个问题,因为这个错误偶尔会出现。
非常感谢!
经过一些调查:
我找到了一些有趣的东西可能会给出这个问题的“神秘”方面的答案:
活动A使用两个游标:一个用于填充EditText字段。另一种是填充ListView。
从活动A移动到活动B并返回时,必须再次填写活动A中的字段+ ListView。似乎EditText字段永远不会有问题。我找不到一种方法来获取EditText字段的当前光标(就像在Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
中)并且原因告诉我EditText字段不会保留它。另一方面,ListView将从上次(从活动A - >活动B之前)“记住”其光标。另外,这很奇怪,Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
在活动B之后会有不同的ID - >活动A和所有这些没有我打电话
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
stopManagingCursor(currentCursor);
我猜在某些情况下,当系统需要释放资源时,光标将被杀死,而当活动B - >活动A,系统仍会尝试使用这个旧的死光标,这将导致异常。在其他情况下,系统将提供一个仍处于活动状态的新游标,因此不会发生异常。这可能解释了为什么有时只出现这种情况。我想这很难调试,因为运行OR调试应用程序时应用程序的速度不同。调试时,需要更多时间,因此可能会给系统时间提供新光标,反之亦然。
根据我的理解,这会使用
Cursor currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
stopManagingCursor(currentCursor);
根据@Martin Stine的建议,在某些情况下必须,在其他情况下冗余:如果我返回方法并且系统正在尝试使用死光标,一个人必须创建一个新的游标并在ListAdapter中替换它,否则我会让一个崩溃的应用程序愤怒的应用程序用户。在另一种情况下,系统会发现自己的新光标 - 上面的行是多余的,因为它们使一个好的光标无效并创建一个新的光标。
我想为了防止这种冗余,我需要这样的东西:
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);
我很想听听你对此的看法!
答案 4 :(得分:2)
只需在光标块的末尾添加以下代码即可。
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);
}
答案 5 :(得分:0)
这个问题困扰了我很长一段时间,我终于提出了一个简单的解决方案,就像所有Android版本的魅力一样。首先,不要使用startManagingCursor(),因为它显然是错误的并且在任何情况下都被弃用。其次,完成后尽快关闭光标。我使用try并最终确保Cursor在所有情况下都被关闭。如果你的方法必须返回一个Cursor,那么调用例程负责尽快关闭它。
我过去常常在活动的生命周期中打开游标,但我已经放弃了这种交易方法。现在我的应用程序非常稳定,并且在切换活动时不会遇到“Android错误:java.lang.IllegalStateException:尝试重新查询已经关闭的游标”,即使它们访问同一个数据库。
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;
}