IllegalStateException “attempt to re-open an already-closed object” in SimpleCursorAdapter from ContentProvider

谁说胖子不能爱 提交于 2019-12-10 15:14:01

问题


I have a series of ListView objects in Fragments that are being populated by a CursorAdapter which gets a Cursor from the LoaderManager for the activity. As I understand it, all database and Cursor close actions are completely handled by the LoaderManager and the ContentProvider, so at no point in any of the code am I calling .close() on anything.

Sometimes, however, I get this exception:

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)

I put some log code into my CursorAdapter that tells me when getView(...), getItem(...) or getItemId(...) are being called and it seems as though this is happening on the first getView(...) for a given adapter after a lot of getView(...)s for another adapter. It also happens after a user has navigated around the app a lot.

This makes me wonder if the Cursor for an adapter is being retained in the CursorAdapter, but being closed in error by the ContentProvider or the Loader. Is this possible? Should I be doing any housekeeping on the CursorAdapter based on app/activity/fragment lifecycle events?

ContentProvider query method:

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;
    }

//...
}

Typical LoaderCallbacks:

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);
    }
};

CursorAdapter constructor:

public ArticlesCursorAdapter(Context context, Cursor c) {
    super(context, c, 0);
    mImageloader = new ImageLoader(context);
}

I have read this question but unfortunately it hasn't got the answer to my problem as it simply suggests using a ContentProvider, which I am.

IllegalStateException: attempt to re-open an already-closed object. SimpleCursorAdapter problems

IMPORTANT NEW INFORMATION THAT HAS JUST COME TO LIGHT

I discovered some other code elsewhere in the project that was NOT using Loaders and NOT managing its Cursors properly. I've just switched this code over to use the same pattern as above; however, if this fixes things, it would suggest that an unmanaged Cursor in one part of a project can kill a properly managed one elsewhere.

Stick around.

OUTCOME OF NEW INFORMATION

That did not fix it.

NEW IDEA

@Override
onDestroyView() {
    getActivity().getLoaderManager().destroyLoader(mFragmentId);
    //any other destroy-time code
    super.onDestroyView()
}

ie possibly yes, I should be doing housekeeping on the CursorAdapter (or rather the CursorLoader in line with lifecycle events).

OUTCOME OF NEW IDEA

Nope.

PREVIOUS IDEA

Turned out to work once I added in a minor tweak! However it's so complex that I should probably rewrite the entire question.


回答1:


Have you updated your data set? It could be the case that the cursor has been re-loaded due notifying a change in the content resolver:

getContentResolver().notifyChange(URI, null);

If you have set a notification URI, this would trigger your current cursor to close and a new cursor to be returned by the cursor loader. You can then grab the new cursor if you have registered a onLoadCompleteListener:

mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
    @Override
    public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
        // Set your listview's CursorAdapter
    }
});



回答2:


You can try to path null instead cursor into adapter constructor. Then owerride SwapCursor(Cursor c) in adapter, move initialization of cursor data there and call it in OnLoadFinished(Loader loader, Cursor data) method of your data loader.

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);
    }


来源:https://stackoverflow.com/questions/14956608/illegalstateexception-attempt-to-re-open-an-already-closed-object-in-simplecur

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!