【问题标题】:Closing Cursor causes StaleDataException on backgrounding fragment with CursorAdapter使用 CursorAdapter 关闭游标会导致后台片段出现 StaleDataException
【发布时间】:2015-12-11 20:09:50
【问题描述】:

这是对我的问题的跟进: How to close a cursor used in a for loop

响应解决了“没有事先关闭的光标完成”警告,但它在非常特殊的情况下导致了 StaleDataException。

如果列表已滚动,则此光标关闭...

Cursor cursor = null;
cursor = (Cursor) getListView().getItemAtPosition(n);
//do something
if(cursor != null) {
    cursor.close();
}

背景片段我收到以下错误:

09-15 21:16:58.240: E/test(21621): Exception
 FATAL EXCEPTION: main
 android.database.StaleDataException: Attempted to access a cursor after it has been closed.
    at android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:64)
    at android.database.BulkCursorToCursorAdaptor.getCount(BulkCursorToCursorAdaptor.java:70)
    at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:196)
    at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162)
    at android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:225)
    at android.widget.AbsListView.onSaveInstanceState(AbsListView.java:1782)
    at android.view.View.dispatchSaveInstanceState(View.java:11950)
    at android.view.ViewGroup.dispatchFreezeSelfOnly(ViewGroup.java:2685)
    at android.widget.AdapterView.dispatchSaveInstanceState(AdapterView.java:782)
    at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:2671)
    at android.view.View.saveHierarchyState(View.java:11933)
    at android.support.v4.app.FragmentManagerImpl.saveFragmentViewState(FragmentManager.java:1608)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1004)
    at android.support.v4.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1212)
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:639)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1478)
    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:446)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4793)

这个问题似乎与一个类似的问题有关:Merging cursors during onLoadFinished() causes StaleDataException after rotation 但它涉及合并游标并建议使用 swapCursor,我不确定如何将其应用于这种情况。

我的想法是getListView().getItemAtPosition(n) 必须返回对游标的引用而不是新游标,因此当活动处于后台并尝试访问现在关闭的游标同时保存片段状态时,它会崩溃。如前所述,它只会在列表视图被滚动时崩溃,我不确定为什么会影响它。

如何正确关闭光标而不导致崩溃?

编辑代码以响应要求查看光标是如何加载的评论:

String[] desired_columns = { 
                MediaStore.Audio.Media._ID,  //this column is needed, even though it won't be displayed so the cursor can populate the listview
                MediaStore.Audio.Media.ARTIST,
                MediaStore.Audio.Media.ALBUM,
                MediaStore.Audio.Media.TITLE,
                MediaStore.Audio.Media.DURATION

                };

        String selectionStatement = MediaStore.Audio.Media.IS_MUSIC + " != ? AND " + MediaStore.Audio.Media.DURATION + " > ?";

        String[] selectionArguments = new String[2];
        selectionArguments[0] = "0";
        selectionArguments[1] = "7000";

        Cursor myCursor = getActivity().getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 
                                                                    desired_columns, 
                                                                    selectionStatement,     //selection criteria
                                                                    selectionArguments,     //selection arguments like with SQL PDO use ? in criteria and put user input here  to avoid sql injection
                                                                    MediaStore.Audio.AudioColumns.ARTIST + " ASC"); //sort order of results
        //moveToFirst() returns false if the cursor is empty
        if (myCursor != null && myCursor.moveToFirst()) {
            customCursorAdapter myCursorAdapter = new customCursorAdapter(getActivity(), myCursor, 0);

            setListAdapter(myCursorAdapter);

            getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
            getListView().setSelector(R.drawable.list_selection_colouring);
        }

【问题讨论】:

  • 我的想法是......你是对的...... 我不知道为什么会影响它。 getView/bindView from adapter调用Cursor.moveToNext/moveToPosition() ... 如何正确关闭光标而不导致崩溃? 如果您不使用 Loaders API ... 那么最简单的方法是使用已弃用的Activity.manageQuery ...其他方式真的很痛苦。
  • @user1977132 分享您如何获取数据并加载到列表的代码
  • 您是否有代码使舒尔在关闭第 n 个光标后再次调用 getListView().getItemAtPosition(n); 返回 null?
  • 添加@Pavan相关代码

标签: android android-fragments android-cursoradapter android-cursor staledataexception


【解决方案1】:

我找到了一个解决方案,它避免了已弃用的 manageQuery 并且也不使用 CursorLoaders。

解决方案是在 onDestroy() 中关闭光标。我尝试在 onPause 内关闭它,但这没有帮助。查看片段生命周期http://developer.android.com/guide/components/fragments.html#Lifecycle onDestroy 几乎是最后调用的方法,因此片段状态已经被保存,从而可以安全地关闭光标。

这是我使用的代码:

@Override
public void onDestroy() {
   super.onDestroy();
   if (cursor != null) {
      cursor.close();
   }
}

【讨论】:

    【解决方案2】:

    试试这个

            Cursor cursor = null;
            try {
                cursor = (Cursor) getListView().getItemAtPosition(n);
                //do something
    
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-01
      相关资源
      最近更新 更多