【发布时间】:2012-05-16 04:18:42
【问题描述】:
我正在尝试通过使用 CursorLoader 和 SimpleCursorTreeAdapter 来异步查询提供程序
这是我的Fragment 类,它实现了CursorLoader
public class GroupsListFragment extends ExpandableListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
private final String DEBUG_TAG = getClass().getSimpleName().toString();
private static final String[] CONTACTS_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME };
private static final String[] GROUPS_SUMMARY_PROJECTION = new String[] {
ContactsContract.Groups.TITLE, ContactsContract.Groups._ID,
ContactsContract.Groups.SUMMARY_COUNT,
ContactsContract.Groups.ACCOUNT_NAME,
ContactsContract.Groups.ACCOUNT_TYPE,
ContactsContract.Groups.DATA_SET };
GroupsAdapter mAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
populateContactList();
getLoaderManager().initLoader(-1, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created.
Log.d(DEBUG_TAG, "onCreateLoader for loader_id " + id);
CursorLoader cl;
if (id != -1) {
// child cursor
Uri contactsUri = ContactsContract.Data.CONTENT_URI;
String selection = "(("
+ ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " NOTNULL) AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.HAS_PHONE_NUMBER
+ "=1) AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " != '') AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID
+ " = ? ))";
String sortOrder = ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
String[] selectionArgs = new String[] { String.valueOf(id) };
cl = new CursorLoader(getActivity(), contactsUri,
CONTACTS_PROJECTION, selection, selectionArgs, sortOrder);
} else {
// group cursor
Uri groupsUri = ContactsContract.Groups.CONTENT_SUMMARY_URI;
String selection = "((" + ContactsContract.Groups.TITLE
+ " NOTNULL) AND (" + ContactsContract.Groups.TITLE
+ " != '' ))";
String sortOrder = ContactsContract.Groups.TITLE
+ " COLLATE LOCALIZED ASC";
cl = new CursorLoader(getActivity(), groupsUri,
GROUPS_SUMMARY_PROJECTION, selection, null, sortOrder);
}
return cl;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in.
int id = loader.getId();
Log.d(DEBUG_TAG, "onLoadFinished() for loader_id " + id);
if (id != -1) {
// child cursor
if (!data.isClosed()) {
Log.d(DEBUG_TAG, "data.getCount() " + data.getCount());
try {
mAdapter.setChildrenCursor(id, data);
} catch (NullPointerException e) {
Log.w("DEBUG","Adapter expired, try again on the next query: "
+ e.getMessage());
}
}
} else {
mAdapter.setGroupCursor(data);
}
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// is about to be closed.
int id = loader.getId();
Log.d(DEBUG_TAG, "onLoaderReset() for loader_id " + id);
if (id != -1) {
// child cursor
try {
mAdapter.setChildrenCursor(id, null);
} catch (NullPointerException e) {
Log.w("TAG", "Adapter expired, try again on the next query: "
+ e.getMessage());
}
} else {
mAdapter.setGroupCursor(null);
}
}
/**
* Populate the contact list
*/
private void populateContactList() {
// Set up our adapter
mAdapter = new GroupsAdapter(getActivity(),this,
android.R.layout.simple_expandable_list_item_1,
android.R.layout.simple_expandable_list_item_1,
new String[] { ContactsContract.Groups.TITLE }, // Name for group layouts
new int[] { android.R.id.text1 },
new String[] { ContactsContract.Contacts.DISPLAY_NAME }, // Name for child layouts
new int[] { android.R.id.text1 });
setListAdapter(mAdapter);
}
}
这是我的适配器,它是SimpleCursorTreeAdapter 的子类
public class GroupsAdapter extends SimpleCursorTreeAdapter {
private final String DEBUG_TAG = getClass().getSimpleName().toString();
private ContactManager mActivity;
private GroupsListFragment mFragment;
// Note that the constructor does not take a Cursor. This is done to avoid
// querying the database on the main thread.
public GroupsAdapter(Context context, GroupsListFragment glf,
int groupLayout, int childLayout, String[] groupFrom,
int[] groupTo, String[] childrenFrom, int[] childrenTo) {
super(context, null, groupLayout, groupFrom, groupTo, childLayout,
childrenFrom, childrenTo);
mActivity = (ContactManager) context;
mFragment = glf;
}
@Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that group
int groupId = groupCursor.getInt(groupCursor
.getColumnIndex(ContactsContract.Groups._ID));
Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);
Loader loader = mActivity.getLoaderManager().getLoader(groupId);
if ( loader != null && loader.isReset() ) {
mActivity.getLoaderManager().restartLoader(groupId, null, mFragment);
} else {
mActivity.getLoaderManager().initLoader(groupId, null, mFragment);
}
}
}
问题是,当我单击父组之一时,三件事中的一件会以一种不一致的方式发生。
1) 要么组打开,孩子出现在它下面
2) 组未打开,setChildrenCursor() 调用引发 NullPointerException 错误,该错误被 try catch 块捕获
3) 组不开,不报错
以下是组展开并显示子项的场景中的一些调试输出:
当显示所有组时,它会输出:
05-20 10:08:22.765: D/GroupsListFragment(22132): onCreateLoader for loader_id -1
05-20 10:08:23.613: D/GroupsListFragment(22132): onLoadFinished() for loader_id -1
-1是组游标的loader_id
然后,如果我特别选择一个组(我们称之为 A 组),它会输出:
05-20 23:22:31.140: D/GroupsAdapter(13844): getChildrenCursor() for groupId 67
05-20 23:22:31.140: D/GroupsListFragment(13844): onCreateLoader for loader_id 67
05-20 23:22:31.254: D/GroupsListFragment(13844): onLoadFinished() for loader_id 67
05-20 23:22:31.254: D/GroupsListFragment(13844): data.getCount() 4
05-20 23:22:31.254: W/GroupsListFragment(13844): Adapter expired, try again on the next query: null
组没有扩展,NullPointerException 被捕获。然后,如果我选择另一个组(我们称之为 B 组),它会输出:
05-20 23:25:38.089: D/GroupsAdapter(13844): getChildrenCursor() for groupId 3
05-20 23:25:38.089: D/GroupsListFragment(13844): onCreateLoader for loader_id 3
05-20 23:25:38.207: D/GroupsListFragment(13844): onLoadFinished() for loader_id 3
05-20 23:25:38.207: D/GroupsListFragment(13844): data.getCount() 6
这一次,NullPointerException 没有被抛出。而不是扩展 B 组,而是扩展 A 组。
谁能解释setChildrenCursor() 调用所表现出的行为?
我认为在onCreateLoader() 中如何实例化组/子 CursorLoaders 存在问题。对于CursorLoader 组,我只想要手机中的所有组。子CursorLoader 应包含组内的所有联系人。有谁知道可能是什么问题?
更新
感谢@Yam 的建议,我现在修改了getChildrenCursor() 方法。我现在选择 groupCursor 位置而不是 ContactsContract.Groups._ID 的值来传递给 initLoader() 调用。我还将逻辑更改为仅在 loader 不为 null 且 loader isReset 为 false 时调用 restartLoader()。
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that
// group
int groupPos = groupCursor.getPosition();
Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
Loader loader = mActivity.getLoaderManager().getLoader(groupPos);
if (loader != null && !loader.isReset()) {
mActivity.getLoaderManager().restartLoader(groupPos, null, mFragment);
} else {
mActivity.getLoaderManager().initLoader(groupPos, null, mFragment);
}
return null;
}
这绝对更有意义,并且不会表现出组有时而不是其他时间扩展的一些不稳定行为。
但是,有些联系人显示在他们不属于的组下。还有一些组中确实有联系人,但不会显示任何联系人。所以看来getChildrenCursor() 问题现在可以解决了。
但现在看来,CursorLoaders 是如何在 onCreateLoader() 方法中实例化的问题。 CursorLoader 是否在 onCreateLoader() 方法中为子光标实例化不正确返回?
更新
所以我发现了我的一个问题。在getChildrenCursor() 方法中,如果我将groupId 传递给initLoader() 方法,那么在onCreateLoader() 方法中,当CursorLoader 创建时,它将获得查询的正确groupid 参数。但是,在onLoadFinished() 中,对setChildrenCursor() 的调用正在传递第一个参数而不是groupPosition 的加载程序ID。我猜我必须将加载器 ID 映射到某些数据结构中的组位置。但我不确定这是否是最好的方法。有人有什么建议吗?
【问题讨论】:
-
我刚刚完成了这个,但它没有使用 CursorLoader,所以这让我很困惑......在我的实现中,getChildrenCursor 返回一个游标。使用 loadermanager,游标/数据实际上在哪里?如果您没有将游标输入到构造函数中,那么将哪些内容作为 groupCursor 输入到
getChildrenCursor? -
在 LoaderManager 的 onLoadFinished() 方法中设置了 groupCursor。我已经使用调试器逐步完成了代码,并且在 getChildenCursor() 方法中,始终定义了 groupCursor。
-
我不知道,听起来你并不总是得到一个填充的 childCursor...
-
填充的 childCursor 到底是什么意思?我怎样才能检查类似的东西?
标签: android simplecursoradapter expandablelistadapter android-cursorloader