CursorLoader и CursorTreeAdapter - PullRequest
       33

CursorLoader и CursorTreeAdapter

3 голосов
/ 02 сентября 2011

Я пытаюсь использовать CursorLoader с CursorTreeAdapter, но столкнулся с проблемой, которую не могу понять.(Если у вас есть рабочий пример этого, не стесняйтесь пропустить остальные и приложите их ниже. Я был бы очень благодарен) В первый раз, когда я открываю группу, все работает нормально.Если я затем закрою и снова открою группу, я получу ошибку переполнения.Вот ошибка:

V/SpellBook(29520): Activity being created 
D/SpellBook(29520): onCreateLoader for id 123456 
V/SpellBook(29520): Resuming 
V/SpellBook(29520): Processing query for uri content://com.zalzala.spellbook.SpellProvider/levels/bard 
D/SpellBook(29520): onLoadFinished() for id 123456 
D/SpellBook(29520): onCreateLoader for id 3 
V/SpellBook(29520): Processing query for uri content://com.zalzala.spellbook.SpellProvider/class/bard/3 
D/SpellBook(29520): onLoadFinished() for id 3 

Здесь все хорошо.Итак, я открыл действие и открыл группу № 3. Вот что произойдет, если я закрою группу 3 и снова открою ее:

D/SpellBook(29520): onLoadFinished() for id 3 
D/SpellBook(29520): onLoadFinished() for id 3 
D/SpellBook(29520): onLoadFinished() for id 3 
D/SpellBook(29520): onLoadFinished() for id 3 
D/SpellBook(29520): onLoadFinished() for id 3 
(... and a lot more of these) 
    E/AndroidRuntime(29520): FATAL EXCEPTION: main 
E/AndroidRuntime(29520): java.lang.StackOverflowError 
E/AndroidRuntime(29520):    at java.util.HashMap.get(HashMap.java:302) 
E/AndroidRuntime(29520):    at android.database.sqlite.SQLiteCursor.getColumnIndex(SQLiteCursor.java:355)
E/AndroidRuntime(29520):    at android.database.CursorWrapper.getColumnIndex(CursorWrapper.java:67) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment$MyExpandableListAdapter.getChildrenCursor(SpellListView.java:216) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:159) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:183) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:1) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:413) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:547) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment$MyExpandableListAdapter.getChildrenCursor(SpellListView.java:217) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:159) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:183) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:1) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:413) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:547) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment$MyExpandableListAdapter.getChildrenCursor(SpellListView.java:217) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:159) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:183) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:1) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:413) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:547) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment$MyExpandableListAdapter.getChildrenCursor(SpellListView.java:217) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:159) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:183) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:1) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:413) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:547) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment$MyExpandableListAdapter.getChildrenCursor(SpellListView.java:217) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:159) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:183) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment.onLoadFinished(SpellListView.java:1) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:413) 
E/AndroidRuntime(29520):    at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:547) 
E/AndroidRuntime(29520):    at com.zalzala.spellbook.SpellListView$ExpandableListCursorLoaderFragment$MyExpandableListAdapter.getChildrenCursor(SpellListView.java:217) 
E/AndroidRuntime(29520):    at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.ja 
W/ActivityManager( 6887):   Force finishing activity com.zalzala.spellbook/.SpellListView

Так что по какой-то причине onLoadFinished () вызывается снова и снова длядочерний курсор при повторном открытии группы.

Вот моя реализация загрузчика:

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // TODO Auto-generated method stub
        Log.d(Spellbook.TAG,"onCreateLoader for id "+id);
        if (id <123456){
            Uri spellUri = Uri.withAppendedPath(SpellProvider.CONTENT_URI, "class");
            spellUri = Uri.withAppendedPath(spellUri, mCLASS);
            spellUri = ContentUris.withAppendedId(spellUri, id);
            return new CursorLoader(getActivity(), spellUri, null, null, null, null);
        }else {
            //get group cursor
            Uri groupUri = Uri.withAppendedPath(SpellProvider.CONTENT_URI, "levels");
            groupUri = Uri.withAppendedPath(groupUri, mCLASS);
            return new CursorLoader(getActivity(), groupUri, null, null, null, null);
        }
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // TODO Auto-generated method stub
        int id = loader.getId();
        Log.d(Spellbook.TAG,"onLoadFinished() for id "+id);
        if (id < 123456){
            //child cursor
            ((CursorTreeAdapter) mAdapter).setChildrenCursor(id, data);
        } else {
            ((CursorTreeAdapter) mAdapter).setGroupCursor(data);
        }

    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // TODO Auto-generated method stub
        int id = loader.getId();
        Log.d(Spellbook.TAG,"onLoaderReset() for id "+id);
        if (id < 123456){
            //child cursor
            ((CursorTreeAdapter) mAdapter).setChildrenCursor(id, null);
        } else {
            ((CursorTreeAdapter) mAdapter).setGroupCursor(null);
        }

    }

Я вызываю CursorTreeAdapter с конструктором, который задает автоматический запрос как false.

    public class MyExpandableListAdapter extends CursorTreeAdapter {

        public MyExpandableListAdapter(Cursor cursor, Context context) {
            super(cursor, context, false); //do not auto requery. pretty sure CursorLoader needs this.
        }

        @Override
        protected Cursor getChildrenCursor(Cursor groupCursor) {
            // Given the group, we return a cursor for all the children within that group 
            int id = groupCursor.getInt(groupCursor.getColumnIndex(SpellDbAdapter.KEY_LEVEL));
            getLoaderManager().initLoader(id, null, ExpandableListCursorLoaderFragment.this);
            return null;
        }

Спасибо за любую помощь!

Ответы [ 5 ]

2 голосов
/ 14 ноября 2015

Это старый вопрос, но до сих пор нет правильного ответа (ну, теперь он есть).

MyExpandableListAdapter имеет метод getChildrenCursor (Cursor):

@Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
    int id = groupCursor.getInt(groupCursor.getColumnIndex(SpellDbAdapter.KEY_LEVEL));
    getLoaderManager().initLoader(id, null, ExpandableListCursorLoaderFragment.this);
    return null;
}

Метод запускает Loader с помощью вызова initLoader. После загрузки данных будет вызван onLoadFinished и установлен дочерний курсор:

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // some code
    ((CursorTreeAdapter) mAdapter).setChildrenCursor(id, data);
    // some more code
}

Метод CursorTreeAdapter.setChildrenCursor выглядит следующим образом:

public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {
    MyCursorHelper childrenCursorHelper = getChildrenCursorHelper(groupPosition);
    childrenCursorHelper.changeCursor(childrenCursor, false);
}

Возвращает childrenCursorHelper с вызовом CursorTreeAdapter.getChildrenCursorHelper:

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

При определенных обстоятельствах childrenCursorHelper еще не существует, и метод теперь вызывает getChildrenCursor (Cursor), который является методом, который запустил всю цепочку вызовов. Логически мы столкнемся со StackOverflowError.

Это ошибка в классе CursorTreeAdapter. В то время как другие рекомендуют вместо этого использовать BaseExpandableAdapter, я рекомендую создать собственный класс CursorTreeAdapter и внести необходимые изменения и исправления.

Ошибка StackOverflowError может быть легко исправлена ​​путем изменения метода getChildrenCursorHelper. Сначала создайте MyCursorHelper, добавьте его в mChildrenCursorHelpers и затем извлекайте дочерний курсор, если для retrieveChildCursor установлено значение true (новый параметр). Вызов метода из setChildrenCursor означает, что мы передаем false, чтобы не попасть в бесконечную рекурсию.

synchronized MyCursorHelper getChildrenCursorHelper(int groupPosition, boolean retrieveChildCursor) {
    MyCursorHelper cursorHelper = mChildrenCursorHelpers.get(groupPosition);

    if (cursorHelper == null && mGroupCursorHelper.moveTo(groupPosition) != null) {
        cursorHelper = new MyCursorHelper(null);
        mChildrenCursorHelpers.put(groupPosition, cursorHelper);

        if (retrieveChildCursor) {
            final Cursor cursor = getChildrenCursor(mGroupCursorHelper.getCursor());
            if (cursor != null) {
                cursorHelper.changeCursor(cursor, false);
            }
        }
    }

    return cursorHelper;
}

Также удалите код в методе MyCursorHelper.deactivate, поскольку CursorLoader заботится о закрытии устаревших курсоров.

1 голос
/ 10 сентября 2011

Хорошо, поэтому, общаясь с кем-то в Google, Расширенные списки, вероятно, давно устарели.Если вам действительно нужно использовать загрузчики курсора и расширенные списки, расширьте базовый адаптер.Если вам это действительно не нужно, продолжайте и не используйте загрузчик курсора.

Не стесняйтесь исправлять меня :)

0 голосов
/ 10 мая 2014

Попробуйте это:

@Override
public void onLoadFinished(Loader<Cursor> loader, final Cursor data) {
    // ...
    final int id = loader.getId();
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            ((CursorTreeAdapter) mAdapter).setChildrenCursor(id, data);
        }
    });
 }
0 голосов
/ 24 января 2013

Я думаю, что когда мы используем initLoader, он заполнит стек, попробуйте вместо этого использовать restartLoader.

@Override
    protected Cursor getChildrenCursor(Cursor groupCursor) {
        // TODO Auto-generated method stub          
        Bundle b = new Bundle();
        //b.putString("Artist", "\""+groupCursor.getString(groupCursor.getColumnIndex(artist.ARTIST))+"\"");
        b.putString("Artist", groupCursor.getString(groupCursor.getColumnIndex(artist.ARTIST)));
        getLoaderManager().restartLoader(groupCursor.getPosition(), b, MpArtist.this);
        return null;
    }
0 голосов
/ 21 февраля 2012

вызов destroyLoader() на onGroupCollapsed(). Но это не решает проблему полностью. Глядя на CursorTreeAdapter реализацию, я уверен, что не буду использовать его с CursorLoader.

Действительно лучше расширить BaseExpandableAdapter. Этот способ позволяет избежать ненужного (в случае использования загрузчиков) использования наблюдателей контента. В моем собственном адаптере я сохраняю созданные загрузчики в разреженном массиве и уничтожаю их при распаде группы. Хорошо работает:)

...