Android - 尝试重新打开一个已经关闭的对象:使用 loaderManager 的 SQLiteQuery

Posted

技术标签:

【中文标题】Android - 尝试重新打开一个已经关闭的对象:使用 loaderManager 的 SQLiteQuery【英文标题】:Android - attempt to re-open an already-closed object: SQLiteQuery using loaderManager 【发布时间】:2013-07-01 17:58:08 【问题描述】:

我对 android 还很陌生,过滤后的 listView 存在一些问题,它的活动从横向模式变为纵向模式,反之亦然。我有一个用于过滤“drinkSearch”的editText,只要我不改变视角(纵向与横向),此过滤就可以工作。这是我得到的错误:

java.lang.IllegalStateException: 尝试重新打开一个已经关闭的 对象:SQLiteQuery:SELECT _id,名称来自饮料

正如您在下面的代码中看到的,我使用接口 LoaderManager.LoaderCallbacks,这个概念对我来说有点新,我不确定 哪里出了问题。我将感谢所有帮助,在此先感谢!

公共类 Drinks_Fragment 扩展 Fragment 实现 LoaderManager.LoaderCallbacks

private static final int DRINKS_LIST_LOADER = 0x01;
private SimpleCursorAdapter adapter;
private ListView drinksList;
private String LOG;
private EditText drinkSearch;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

    View view = inflater.inflate(R.layout.drinks_list, container, false);
    drinkSearch = (EditText)view.findViewById(R.id.drinkInputSearch);
    drinksList = (ListView) view.findViewById(R.id.drinksList);
    drinksList.setEmptyView(view.findViewById(R.id.empty_list_view));

    String[] from = DrinksTable.COLUMN_NAME;
    int[] to = R.id.drinkName;
    getLoaderManager().initLoader(DRINKS_LIST_LOADER, null, this);
    adapter = new SimpleCursorAdapter(getActivity().getApplicationContext(), R.layout.drinks_list_item,null, from, to, 0);
    drinksList.setAdapter(adapter);

在这一部分中,我根据在 searchDrink 编辑文本中输入的字符串向我的 contentProvider 请求一个新的光标。 (如下代码,直到“返回视图”就在上面部分的下方,与onCreateView方法相同)

    drinkSearch.addTextChangedListener(new TextWatcher() 

        @Override
        public void onTextChanged(CharSequence s, int arg1, int arg2, int arg3) 
            // When user changed the Text
            adapter.getFilter().filter(s.toString());
        

        @Override
        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) 
            // TODO Auto-generated method stub

        

        @Override
        public void afterTextChanged(Editable arg0) 
            // TODO Auto-generated method stub
        
    );

    adapter.setFilterQueryProvider(new FilterQueryProvider() 

        public Cursor runQuery(CharSequence constraint) 
            String value = "%"+constraint.toString()+"%";
            ContentResolver content = getActivity().getContentResolver();
            return content.query(CupProvider.DRINKS_URI,new String[]DrinksTable.COLUMN_ID,DrinksTable.COLUMN_NAME,DrinksTable.COLUMN_NAME + " LIKE ?",new String[]value,null);
        
    );

    return view;


@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) 
    super.onCreateContextMenu(menu,v,menuInfo);
    MenuInflater inflater = getActivity().getMenuInflater();
    inflater.inflate(R.menu.drink_actions,menu);



@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) 
    String[] projection = DrinksTable.COLUMN_ID, DrinksTable.COLUMN_NAME;
    CursorLoader cursorLoader = new CursorLoader(getActivity(), CupProvider.DRINKS_URI, projection, null, null, null);
    return cursorLoader;


@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) 
    adapter.swapCursor(cursor);



@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) 
    // data is not available anymore, delete reference
    adapter.swapCursor(null);

这里有 2 张图片来展示它目前的样子:http://oi42.tinypic.com/dfc702.jpg http://oi43.tinypic.com/2ylqkqa.jpg

【问题讨论】:

【参考方案1】:

由于格式不佳,您的代码有点难以理解。

无论如何,提供的答案实际上不是修复。在onLoadFinished 返回的游标应该保证不会被关闭,因此您以错误的方式加载游标。具体来说,当你打电话时

adapter.getFilter().filter(s.toString());

我不太明白这里发生了什么,但我明白你应该做点别的事情。只需将查询过滤器存储在Fragment 的字段中并运行getLoaderManager().restartLoader(DRINKS_LIST_LOADER, null, this);。请注意,您运行的是restartLoader,而不是initLoader,因为您有不同的数据要查询。

在您的onCreateLoader 中,您应该使用存储为selection 的实例变量的过滤器。

一些背景

initLoader 加载上次运行时加载的数据(如果之前运行过)。这就是为什么您在片段/活动的初始化方法中调用。这很方便,因为您不必重新查询方向更改。

restartLoader 清理以前加载的数据,以便您获得一个新的Loader 来处理(可能)不同的数据。


如果您仍然不确定自己在做什么,请务必阅读this article,这是一篇关于加载器的非常好的介绍性文章,其中包含示例代码,看起来非常像您想要实现的目标。装载机一开始非常神秘,但一旦掌握了窍门,就会一帆风顺。

【讨论】:

【参考方案2】:

已修复:

@覆盖 public void onLoadFinished(Loader cursorLoader, Cursor cursor) if(!cursor.isClosed()) 适配器.swapCursor(光标);

我现在确实有另一个问题,我的列表没有自动更新...

【讨论】:

【参考方案3】:

在 AndroidManifest 中添加这一行在同样的情况下帮助了我:

android:configChanges="keyboardHidden|orientation|screenSize"

【讨论】:

【参考方案4】:

这是因为过滤后,原来的游标被关闭了,因为调用了changeCursor。覆盖 changeCursor :

super.swapCursor(cursor)

在您的 SimpleCursorAdapter 中确保原始光标由 CursorLoader 管理。

【讨论】:

以上是关于Android - 尝试重新打开一个已经关闭的对象:使用 loaderManager 的 SQLiteQuery的主要内容,如果未能解决你的问题,请参考以下文章

来自 ContentProvider 的 SimpleCursorAdapter 中的 IllegalStateException“尝试重新打开已经关闭的对象”

关闭并重新打开应用程序时Android崩溃

Android实现微信小游戏关闭后再次打开接着玩,无需重新加载

如何重新打开已关闭的文件描述符

防止 Android 重新创建已经存在的活动

Android如何在屏幕关闭/打开时防止webview重新加载?