Xamarin.Forms CollectionView获取联系人时遇Android.StaleDataException异常
这个异常的核心原因是:你在GetAllAsync()方法中大概率提前关闭了Cursor,或者Cursor的生命周期短于CollectionView数据绑定的生命周期,导致CollectionView后续尝试访问数据时,Cursor已经被关闭。
以下是具体修复步骤:
将Cursor数据转换为内存对象集合
不要直接把Cursor绑定给CollectionView的ItemsSource,而是在GetAllAsync()里把Cursor中的所有联系人数据读取到自定义的实体集合(比如List<Contact>)中,再返回这个内存集合。这样可以安全关闭Cursor,避免后续访问已关闭的Cursor。
示例代码:public async Task<List<Contact>> GetAllAsync() { var contacts = new List<Contact>(); Cursor cursor = null; try { // 替换为你获取联系人Cursor的实际逻辑 cursor = ContentResolver.Query(ContactsContract.Contacts.ContentUri, null, null, null, null); if (cursor?.MoveToFirst() ?? false) { do { var contact = new Contact { Id = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts._ID)), DisplayName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.DisplayName)) // 根据需求读取其他联系人字段 }; contacts.Add(contact); } while (cursor.MoveToNext()); } } finally { // 确保Cursor被正确关闭,避免资源泄漏 cursor?.Close(); } return contacts; }绑定内存集合到CollectionView
在页面或ViewModel中,调用GetAllAsync()拿到List<Contact>后,直接将该集合赋值给CollectionView的ItemsSource,而非Cursor。规范Cursor的使用
所有Cursor操作务必放在try-finally块中,确保无论是否抛出异常,Cursor都会被关闭;不要将Cursor暴露到方法外部,避免外部代码在Cursor关闭后尝试访问。
额外提醒:
如果使用第三方联系人库或Xamarin工具类获取联系人,检查其内部是否正确处理了Cursor生命周期。若存在同样问题,需手动封装转换为内存集合。如果需要实时更新联系人数据,可通过ContentObserver监听联系人变化,再重新获取内存集合并刷新CollectionView,不要长期保持Cursor打开状态。
内容的提问来源于stack exchange,提问作者Suthi Yuvaraj

