我从某些用户那里得到ACRA异常报告,说明为我的appwidget(RemoteViewService
)提供数据的游标已停用/关闭。它从来没有发生在我身上,但它发生在一个有点问题的地方。
这是我的RemoteViewService的代码:
public static class ListItemService extends RemoteViewsService {
public RemoteViewsFactory onGetViewFactory(final Intent intent) {
return new RemoteViewsFactory() {
private MyCursor cursor;
public void onCreate() {
// Nothing
}
public synchronized void onDestroy() {
if (this.cursor != null)
this.cursor.close();
}
public synchronized RemoteViews getViewAt(int position) {
// Here I read from the cursor and it crashes with
// the stack trace below
}
public int getCount() {
return ((this.cursor != null) ? this.cursor.getCount() : 0);
}
public int getViewTypeCount() {
return 1;
}
public boolean hasStableIds() {
return true;
}
public long getItemId(int position) {
return position;
}
public RemoteViews getLoadingView() {
return null;
}
public synchronized void onDataSetChanged()
{
if (this.cursor != null)
this.cursor.close();
this.cursor = getApplicationCntext().getContentResolver().query(myUri, null, null, null, null);
}
};
堆栈跟踪因平台版本而异。例如,我在4.0.3上得到以下内容:
android.database.StaleDataException: Attempting to access a closed CursorWindow.Most probable cause: cursor is deactivated prior to calling this method.
在2.3上,我得到了一个:
java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery
除了onDestroy()
和onDataSetChanged()
之外,我不能在我的生活中找出关闭光标的人或者什么。一些用户报告说他们在发生崩溃时没有积极地使用应用程序。
我怀疑对ContentProvider的多次调用可能会返回相同的光标,当我显示使用相同查询的UI时,它们会相互踩踏。但由于光标对象不同,情况似乎并非如此。
有什么想法吗?
答案 0 :(得分:1)
在我看来,就像多个线程之间的同步问题一样,一个关闭前一个光标,另一个立即访问它。
您可能需要考虑首次关闭光标,例如onPause事件。
此外 - 您可以添加安全预防措施,在再次访问之前检查cursor.isClosed()。您还可以为代码添加一些同步。
一个辅助方法,用一个本地var获取新游标,只有一次完成,替换前一个并在此期间关闭它可能是一个更快的解决方案。
来自AOSP doc;
此接口提供对结果集的随机读写访问 由数据库查询返回。不需要游标实现 要同步所以代码使用多个线程的Cursor应该 使用Cursor时执行自己的同步。
ContentResolver.query()客户端方法始终返回一个Cursor,其中包含查询为与查询的选择条件匹配的行的投影指定的列。 Cursor对象提供对其包含的行和列的随机读取访问。使用Cursor方法,您可以迭代结果中的行,确定每列的数据类型,从列中获取数据,以及检查结果的其他属性。一些Cursor实现在提供者的数据更改时自动更新对象,或者在Cursor更改时触发观察者对象中的方法,或两者都是。
答案 1 :(得分:0)
尝试在this.cursor = null
方法
onDestory()
public synchronized void onDestroy() {
if (this.cursor != null){
this.cursor.close();
this.cursor = null
}
}