Android 游标装入器和游标转接器

Android 游标装入器和游标转接器,android,adapter,expandablelistview,android-cursorloader,Android,Adapter,Expandablelistview,Android Cursorloader,我正在尝试将CursorLoader与CursorTreeAdapter一起使用,但遇到了一个无法解决的问题。(如果您有一个这样的工作示例,请跳过其余部分并将其附在下面。我将非常感谢) 我第一次组建团队时,一切都很顺利。如果随后关闭并重新打开组,则会出现溢出错误。以下是错误: V/SpellBook(29520): Activity being created D/SpellBook(29520): onCreateLoader for id 123456 V/SpellBook(29520

我正在尝试将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);
        }

    }

谢谢你的帮助

好吧,跟谷歌的某个人说,扩展列表可能很久以前就应该被弃用了。如果确实需要使用光标加载器和扩展列表,请扩展基本适配器。如果您真的不需要它,请不要使用游标加载器

请随时纠正我:)

onGroupCollapsed()
上调用
destroyLoader()
。但这并不能完全解决问题。查看
CursorTreeAdapter
实现,确保不要将其与
CursorLoader
一起使用


扩展
BaseExpandableAdapter
确实更好。这样可以避免不必要的(在使用加载器的情况下)使用内容观察者。在我的自定义适配器中,我将创建的加载程序保持在稀疏数组中,并在组崩溃时销毁它们。工作良好:)

我认为当我们使用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;
    }
试试这个:

@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);
        }
    });
 }
@覆盖
public void onLoadFinished(加载程序,最终光标数据){
// ...
final int id=loader.getId();
new Handler().post(new Runnable()){
@凌驾
公开募捐{
((CursorTreeAdapter)mAdapter).setChildrenCursor(id,数据);
}
});
}

这是一个老问题,但仍然没有正确答案(现在有了)

MyExpandableListAdapter有一个getChildrenCursor(游标)方法:

该方法使用initLoader调用启动加载程序。加载数据后,将调用onLoadFinished并设置子光标:

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // some code
    ((CursorTreeAdapter) mAdapter).setChildrenCursor(id, data);
    // some more code
}
它通过调用CursorTreeAdapter.getChildrenCursorHelper检索childrenCursorHelper:

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),这是启动整个调用链的方法。从逻辑上讲,我们将遇到StackOverflower错误

这是CursorTreeAdapter类中的错误。虽然其他人建议改用BaseExpandableAdapter,但我建议创建一个自定义的CursorTreeAdapter类,并进行必要的更改和修复

StackOverflowerError可以通过更改getChildrenCursorHelper方法轻松修复。首先创建MyCursorHelper,将其添加到McChildrenCursorHelpers,然后在retrieveChildCursor设置为true(新参数)时检索子光标。从setChildrencersor调用该方法意味着传递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方法中的代码,因为游标加载程序负责关闭过时的游标。

ExpandableList不推荐使用?我不这么认为,也许你说的是CursorTreeAdapter…但这不是违背了使用加载器的初衷吗?忘了我的答案吧。也许我应该删除它。查看CursorTreeAdapter实现,确保不要将其与CursorLoader一起使用。扩展基本可扩展适配器确实更好。实际上,这是一个非常好的建议。扩展基本可扩展适配器可能是一种方法。而且这种方法可以避免不必要的(在使用加载程序的情况下)使用内容观察者。在我的自定义适配器中,我将创建的加载程序保持在稀疏阵列中,并在组崩溃时销毁它们。很好:)酷。如果您不介意的话,请更新您的答案,我会接受。我尝试了类似的实现,但无法使其正常工作。我不是子类化CursorTreeAdapter,而是子类化SimpleCursorTreeAdapter。groupCursor加载良好,我可以看到所有组,但是当我单击其中一个组时,它调用getChildrenCursor()子例程,该子例程返回null。我得到一个空指针表达式错误。你遇到过这个问题吗?听起来这更像是你如何得到孩子的问题。您能验证获取子游标的调用在测试中是否有效吗?我发现问题不在于getChildrenCursor()子例程,而在于对setChildrenCursor()子例程的调用。因此,在我的onLoadFinished()方法中,我获取加载程序的id。如果它是一个子游标,那么我已经将setChildrenCursor放在一个try-catch块try{mAdapter.setChildrenCursor(id,data);}catch(NullPointerException e){Log.w(“标记”,“适配器已过期,请在下一个查询中重试:“+e.getMessage());}现在,当抛出空指针异常时,我正在捕获它。但是,只有一些组被展开以显示子组。我在这里做了类似的事情,我在SDK示例中遵循了这个文件
ApiDemos/src/com/example/android/api/view/ExpandableList2.java
,我的列表运行良好…
public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {
    MyCursorHelper childrenCursorHelper = getChildrenCursorHelper(groupPosition);
    childrenCursorHelper.changeCursor(childrenCursor, false);
}
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;
}
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;
}