SimpleCursorAdapter异常:java.lang.IllegalStateException: 尝试重查已关闭游标
解决AutoCompleteTextView+SimpleCursorAdapter后台返回后出错的问题
这种场景我之前也碰到过,核心问题就是Cursor的生命周期没有和Activity的生命周期正确绑定。当你按下Home键把应用切到后台时,原来的Cursor可能已经被系统回收或者资源被释放了,等再回到应用时,Adapter还拿着失效的Cursor,自然就会触发错误。下面给你两种靠谱的解决方案:
方案一:手动管理Cursor生命周期
如果你不想改动太多现有代码,可以在Activity的生命周期方法里手动刷新Cursor:
- 先在Activity里定义成员变量:
private SimpleCursorAdapter mAutoCompleteAdapter; private Cursor mCursor;
- 在
onCreate里初始化Adapter(先传nullCursor):
// 初始化Adapter,布局和字段映射根据你的实际情况调整 mAutoCompleteAdapter = new SimpleCursorAdapter( this, android.R.layout.simple_dropdown_item_1line, null, new String[]{"你的数据库字段名"}, new int[]{android.R.id.text1}, 0 ); yourAutoCompleteTextView.setAdapter(mAutoCompleteAdapter); // 第一次加载Cursor loadFreshCursor();
- 写一个通用的加载Cursor方法:
private void loadFreshCursor() { // 先关闭旧的Cursor,避免内存泄漏 if (mCursor != null) { mCursor.close(); } // 重新查询SQLite获取新的Cursor mCursor = yourSQLiteDatabase.query( "你的表名", new String[]{"_id", "你的字段名"}, // 注意必须包含_id,SimpleCursorAdapter要求 "匹配条件", new String[]{你的查询参数}, null, null, "排序规则" ); // 替换Adapter里的旧Cursor mAutoCompleteAdapter.swapCursor(mCursor); }
- 在
onResume里重新加载Cursor,确保回到应用时用的是新鲜的Cursor:
@Override protected void onResume() { super.onResume(); loadFreshCursor(); }
- 在
onPause里关闭Cursor,释放资源:
@Override protected void onPause() { super.onPause(); if (mCursor != null) { mCursor.close(); mCursor = null; } // 让Adapter不再持有Cursor引用 mAutoCompleteAdapter.swapCursor(null); }
方案二:用CursorLoader自动管理(推荐)
CursorLoader是Android官方推荐的Cursor管理工具,它会自动和Activity/Fragment的生命周期绑定,不用你手动关Cursor、刷新,省心很多:
- 让你的Activity实现
LoaderManager.LoaderCallbacks<Cursor>接口:
public class YourActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> { private SimpleCursorAdapter mAutoCompleteAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); // 初始化Adapter,同样先传null Cursor mAutoCompleteAdapter = new SimpleCursorAdapter( this, android.R.layout.simple_dropdown_item_1line, null, new String[]{"你的字段名"}, new int[]{android.R.id.text1}, 0 ); yourAutoCompleteTextView.setAdapter(mAutoCompleteAdapter); // 初始化Loader getSupportLoaderManager().initLoader(0, null, this); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // 返回CursorLoader,配置你的查询参数 return new CursorLoader( this, 你的ContentProviderURI, // 如果用ContentProvider的话,或者直接用SQLiteQueryBuilder构建查询 new String[]{"_id", "你的字段名"}, "匹配条件", new String[]{你的查询参数}, "排序规则" ); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // 加载完成后替换Adapter的Cursor mAutoCompleteAdapter.swapCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { // Loader重置时清空Cursor mAutoCompleteAdapter.swapCursor(null); } }
用CursorLoader的好处是,当应用从后台回到前台时,它会自动重新查询获取最新的Cursor,完全不用你手动处理生命周期的细节,还能避免内存泄漏。
关键注意点
- SimpleCursorAdapter要求Cursor必须包含
_id字段,否则会报错,你的查询语句里一定要加上这个字段。 - 永远不要在主线程之外的线程直接操作UI,如果查询数据量较大,建议把查询逻辑放到异步任务里,CursorLoader本身就是异步实现的,不用额外处理。
内容的提问来源于stack exchange,提问作者Sergey Emeliyanov
相关产品推荐
相关产品推荐

