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:已经有一个侦听器注册了