Search code examples
androidandroid-contentproviderandroid-orientationandroid-cursorloader

Fragments, ContentProviders and cursors on orientation change


Please help me understand what's happening here.

I have two fragments (A and B) in tabs reading data from different tables, via a CursorLoader and aContentProvider, in a Sqlite-database. With different URIs I can change which table the ContentProvider is quering.

I works as expected when switch between the tabs A and B, unless I switch to B, rotate and switches back to A the wrong cursor is returned. The cursor from fragment B is returned instead of a cursor for fragment A with makes the listView in fragment A to cause a crash. In some way the cursor seems to be reused on a rotation.

Why is this happening and how can I prevent that the wrong cursor is returned?

This is what I have in both fragment A and B. Tried to assing a loader id with no success.

public void onResume() {
    super.onResume();
    getLoaderManager().restartLoader(mLoaderId, null, this);
}

My ContentProvider looks like this:

public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {

    SQLiteDatabase db = dbHelper.getWritableDatabase();
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

    Cursor cursor = null;       

    switch (uriMatcher.match(uri)) {
    case ALL_NEWS:
        queryBuilder.setTables(NewsDb.SQLITE_TABLE);
        cursor = queryBuilder.query(db, projection, selection,
                selectionArgs, null, null, sortOrder);
        break;

    case SINGLE_PLACE:
        queryBuilder.setTables(PlacesDb.SQLITE_TABLE);
        String id = uri.getPathSegments().get(1);
        queryBuilder.appendWhere(PlacesDb.KEY_ID + "=" + id);
        cursor = queryBuilder.query(db, projection, selection,
                selectionArgs, null, null, sortOrder);
        break;

    default:
        throw new IllegalArgumentException("Unsupported URI: " + uri);
    }


    return cursor;

}

Solution

  • When using CursorLoader, data changes are automatically observed, so you should definitely remove the restartLoader call in your onResume(). If implemented properly, you should only have to call initLoader in onActivityCreated of the Fragment.

    LoaderManager IDs are only scoped to the Fragment, so you should be using a static constant ID. If the Loaders are handled in the Fragments, themselves, it's perfectly fine for every Fragment to have the same Loader ID (even if they're managed by the same Activity).

    So in each Fragment:

    private static final int LOADER_ID = 0;
    
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    
        // ...
    
        getLoaderManager().initLoader(LOADER_ID, null, this);
    }