Android “非法国家例外”;尝试重新打开已关闭的对象“;在来自ContentProvider的SimpleCorsorAdapter中
我在Android “非法国家例外”;尝试重新打开已关闭的对象“;在来自ContentProvider的SimpleCorsorAdapter中,android,android-contentprovider,android-cursoradapter,android-loadermanager,android-loader,Android,Android Contentprovider,Android Cursoradapter,Android Loadermanager,Android Loader,我在Fragments中有一系列ListView对象,这些对象由CursorAdapter填充,它从LoaderManager获取活动的光标。据我所知,所有数据库和游标关闭操作都完全由LoaderManager和ContentProvider处理,因此在任何代码中,我都不会对任何东西调用.close() mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() { @Override
Fragment
s中有一系列ListView
对象,这些对象由CursorAdapter
填充,它从LoaderManager
获取活动的光标。据我所知,所有数据库和游标
关闭操作都完全由LoaderManager
和ContentProvider
处理,因此在任何代码中,我都不会对任何东西调用.close()
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
然而,有时我会遇到这样的例外:
02-19 11:07:12.308 E/AndroidRuntime(18777): java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery (mSql = SELECT * FROM privileges WHERE uuid!=?)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:33)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:82)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:147)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:178)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162)
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.widget.CursorAdapter.getView(CursorAdapter.java:241)
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
我将一些日志代码放入我的CursorAdapter
中,它告诉我什么时候调用getView(…)
,getItem(…)
或getItemId(…)
,对于另一个适配器,在进行了大量getView(…)
之后,对于给定的适配器,这似乎是在第一个getView(…)
上发生的。用户经常浏览应用程序后,也会发生这种情况
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
这使我想知道适配器的光标是否保留在光标适配器中,但被内容提供程序或加载程序错误关闭。这可能吗?我是否应该根据应用程序/活动/片段生命周期事件在游标或适配器上执行任何内务管理
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
ContentProvider
查询方法:
class MyContentProvider extends ContentProvider {
//...
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor query = db.query(getTableName(uri), projection, selection, selectionArgs, null, null, sortOrder);
query.setNotificationUri(getContext().getContentResolver(), uri);
return query;
}
//...
}
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
典型的装载机回拨
:
LoaderCallbacks<Cursor> mCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mArticleAdapter.swapCursor(null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if(cursor.isClosed()) {
Log.d(TAG, "CURSOR RETURNED CLOSED");
Activity activity = getActivity();
if(activity!=null) {
activity.getLoaderManager().restartLoader(mFragmentId, null, mCallbacks);
}
return;
}
mArticleAdapter.swapCursor(cursor);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
triggerArticleFeed();
CursorLoader cursorLoader = null;
if(id == mFragmentId) {
cursorLoader = new CursorLoader(getActivity(),
MyContentProvider.ARTICLES_URI,
null,
ArticlesContentHelper.ARTICLES_WHERE,
ArticlesContentHelper.ARTICLES_WHEREARGS,
null);
}
return(cursorLoader);
}
};
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
我读过这个问题,但不幸的是,它没有得到我问题的答案,因为它只是建议使用内容提供者
,我就是这样
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
刚刚曝光的重要新信息
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
我在项目的其他地方发现了一些代码,它们没有使用Loader
s,也没有正确管理其光标。我刚刚将这段代码转换为使用与上面相同的模式;但是,如果这样做可以解决问题,则表明项目某个部分中的非托管游标
可能会在其他地方杀死正确管理的游标
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
留下来
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
新信息的结果
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
这并没有解决问题
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
新想法
@Override
onDestroyView() {
getActivity().getLoaderManager().destroyLoader(mFragmentId);
//any other destroy-time code
super.onDestroyView()
}
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
也就是说,可能是的,我应该在CursorAdapter
(或者更确切地说是CursorLoader
上根据生命周期事件进行内务管理)
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
新想法的结果
@Override
onDestroyView() {
getActivity().getLoaderManager().destroyLoader(mFragmentId);
//any other destroy-time code
super.onDestroyView()
}
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
没有
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
以前的想法
@Override
onDestroyView() {
getActivity().getLoaderManager().destroyLoader(mFragmentId);
//any other destroy-time code
super.onDestroyView()
}
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
结果是工作,一旦我加入了一个小调整!但是它太复杂了,我可能应该重写整个问题。您更新了数据集吗?可能是由于通知内容解析程序中的更改而重新加载了光标:
getContentResolver().notifyChange(URI, null);
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
如果您已经设置了通知URI,这将触发当前游标关闭,并触发游标加载程序返回一个新游标。如果已注册onLoadCompleteListener,则可以获取新光标:
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
mCursorLoader.registerListener(0,新的OnLoadCompleteListener(){
@凌驾
public void onLoadComplete(加载器、光标){
//设置listview的光标或适配器
}
});
您可以尝试将null游标路径改为进入适配器构造函数。然后在适配器中的owerride SwapCursor(游标c),将游标数据的初始化移动到那里,并在数据加载器的OnLoadFinished(Loader Loader,游标数据)方法中调用它
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Set your listview's CursorAdapter
}
});
enter code here
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// ... building your query here
mSimpleCursorAdapter = new mSimpleCursorAdapter(getActivity().getApplicationContext(),
layout, null, from, to, flags);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
contentAdapter.swapCursor(data);
}
在此处输入代码
@凌驾
已创建ActivityState上的公共无效(Bundle savedInstanceState){
//…在此处生成您的查询
mSimpleCursorAdapter=新的mSimpleCursorAdapter(getActivity().getApplicationContext(),
布局,null、from、to标志);
}
@凌驾
public void onLoadFinished(加载器、光标数据){
contentAdapter.swapCursor(数据);
}
给我们看一些代码ContentProvider.query
和OnLoadCompleteListener.onLoadComplete
确定更多信息。。。在onLoadComplete
中,只需使用Adapter.swapCursor(newCursor)代码>。。。下一个不要自己在适配器中存储游标实例,只需在适配器内部使用getCursor()。。。因此,当光标交换后,您将获得新的光标实例…@Selvin这就是我正在做的,我只是直接使用传递到bindView(…)
中的光标。当光标被CursorAdapter
代码(我还没有重写)移动到位时,崩溃实际上就来了。哦,没有OnLoadCompleteListener
,我正在实现LoaderCallbacks
。添加到ContentProvider>查询(…)
和一个典型的LoaderCallbacks
。周围有很多这样的URI,但它们看起来都差不多。我正在使用它,并且我已经设置了光标
来接收关于该URI的通知。我以为CursorAdapter
应该自动处理这个问题?我已经在那里实现了LoaderCallbacks
并调用CursorAdapter\swapCursor(…)
。实际上,我认为这可能是相关的:我正在调用notifyChange(…)
。但是,您通常会期望加载程序在加载程序回调中点击onLoadFinished(…)
,如果不是,它会在onloadcompletelelistener中点击onLoadComplete(…)
?这看起来有点像复制品。如果需要的话,请告诉我。我产生了一个新问题:注册这两种类型的侦听器似乎都是一个杀手:02-19 17:46:25.139:E/AndroidRuntime(24886):java.lang.IllegalStateException:已经有一个侦听器注册了