我正在尝试将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,该构造函数将auto requery指定为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;
}
感谢您的帮助!
答案 0 :(得分:2)
这是一个老问题,但仍然没有正确答案(现在已经存在)。
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;
}
该方法使用initLoader调用启动Loader。加载数据后,将调用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);
}
它通过调用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),这是启动整个调用链的方法。从逻辑上讲,我们将遇到StackOverflowError。
这是CursorTreeAdapter类中的错误。虽然其他人建议使用BaseExpandableAdapter,但我建议创建一个自定义的CursorTreeAdapter类并进行必要的更改和修复。
通过更改getChildrenCursorHelper方法可以轻松修复StackOverflowError。首先创建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 :(得分:1)
好的,所以在谷歌与某人交谈时,扩展列表可能很久以前就已被弃用了。如果您确实需要使用游标加载器和扩展列表,请扩展基本适配器。如果你真的不需要它,请继续使用游标加载器。
随意纠正我:)
答案 2 :(得分:0)
在destroyLoader()
上致电onGroupCollapsed()
。但它并没有完全解决问题。查看CursorTreeAdapter
实现,请确保不要将其与CursorLoader
一起使用。
扩展BaseExpandableAdapter
真的更好。这种方式允许避免不必要的(在使用加载器的情况下)内容观察者的使用。在我的自定义适配器中,我保持稀疏数组中的创建加载器并在组崩溃时销毁它们。效果很好:))
答案 3 :(得分:0)
我认为当我们使用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;
}
答案 4 :(得分:0)
试试这个:
@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);
}
});
}