Maarten
Maarten

Reputation: 7318

CursorTreeAdapter.setChildrenCursor() results in loop

I guess I do not understand correctly how to use CursorTreeAdapter.setChildrenCursor(), because I get a StackOverflowError, because of what seems like a loop in my LogCat.

The pattern in the loop is:

  1. my implementation of getChildrenCursor() restarts a Loader
  2. CursorTreeAdapter.setChildrenCursor() calls CursorTreeAdapter.getChildrenCursorHelper(), which
  3. goes back to my implementation getChildrenCursor.

I suspect that Android's source code may exhibit incorrect behaviour (see final code snippet).


Here's the relevant methods in my code:

CursorTreeAdapter:

        /**
        * Return null, but set off a Loader that will set the Cursor after asynchronous loading
        **/
        @Override
        protected Cursor getChildrenCursor(Cursor groupCursor) {
                getLoaderManager().initLoader(groupCursor.getInt(0), null, CollectionsMasterFragment.this);
                return null;
        }

LoaderCallbacks:

@Override
public void onLoadFinished(android.support.v4.content.Loader loader, Cursor cursor) {
    d("onLoadFinished with "
            + cursor.getCount() + " elements");
    // Set children cursor to correct group
    switch (loader.getId()) {
        case POS_0:
            mCollectionsAdapter.setChildrenCursor(POS_0, cursor);
            break;
        case POS_1:
            mCollectionsAdapter.setChildrenCursor(POS_1, cursor);
            break;
        default:
            throw new IllegalArgumentException(
                    "Could not handle loader id " + loader.getId());
    }
}

@Override
public android.support.v4.content.Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
    switch (id) {
        case POS_0:
            d("Creating new dossiers loader");
            return new Model.Dossier.Loader(getActivity());
        case POS_1:
            d("Creating new bundles loader");
            return new Model.Bundle.Loader(getActivity());
        default:
            throw new IllegalArgumentException(
                    "Could not handle loader id " + id);
    }
}

Here's Android 4.4's code for CursorTreeAdapter:

 /**
 * Sets the children Cursor for a particular group. If there is an existing cursor
 * it will be closed.
 * <p>
 * This is useful when asynchronously querying to prevent blocking the UI.
 * 
 * @param groupPosition The group whose children are being set via this Cursor.
 * @param childrenCursor The Cursor that contains the children of the group.
 */
public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {

    /*
     * Don't request a cursor from the subclass, instead we will be setting
     * the cursor ourselves.
     */
    MyCursorHelper childrenCursorHelper = getChildrenCursorHelper(groupPosition, false);

    /*
     * Don't release any cursor since we know exactly what data is changing
     * (this cursor, which is still valid).
     */
    childrenCursorHelper.changeCursor(childrenCursor, false);
}

/**
 * Gets the cursor helper for the children in the given group.
 * 
 * @param groupPosition The group whose children will be returned
 * @param requestCursor Whether to request a Cursor via
 *            {@link #getChildrenCursor(Cursor)} (true), or to assume a call
 *            to {@link #setChildrenCursor(int, Cursor)} will happen shortly
 *            (false).
 * @return The cursor helper for the children of the given group
 */
synchronized MyCursorHelper getChildrenCursorHelper(int groupPosition, boolean requestCursor) {
    MyCursorHelper cursorHelper = mChildrenCursorHelpers.get(groupPosition);

    if (cursorHelper == null) {
        if (mGroupCursorHelper.moveTo(groupPosition) == null) return null;

        final Cursor cursor = getChildrenCursor(mGroupCursorHelper.getCursor());
        cursorHelper = new MyCursorHelper(cursor);
        mChildrenCursorHelpers.put(groupPosition, cursorHelper);
    }

    return cursorHelper;
}

As you can see, the parameter requestCursor for getChildrenCursorHelper is ignored, so that getChildrenCursor is called again!

What's up with this? Is this a bug? Shouldn't setChildrenCursor just perform childrenCursorHelper = new MyCursorHelper(childrenCursor), instead of asking the adapter for the childrenCursor?!

Upvotes: 2

Views: 637

Answers (1)

Pomagranite
Pomagranite

Reputation: 696

I am struggling also trying to populate an expandable listview from a loader, however I think part of your problem has to do with the fact you must implement multiple loaders for each type or schema, see if this link helps*Set multiple cursor loaders with multiple adapters - Android*

Upvotes: 1

Related Questions