1

これは私の質問へのフォローアップです: 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)

この質問は同様の問題に関連しているようです: onLoadFinished() 中にカーソルをマージすると、回転後に StaleDataException が発生します が、カーソルのマージに関連しており、swapCursor の使用を提案しています。この状況にどのように適用するかわかりません。

私の考えではgetListView().getItemAtPosition(n)、新しいカーソルではなくカーソルへの参照を返す必要があるため、アクティビティがバックグラウンドであり、フラグメントの状態を保存しているときに現在閉じられているカーソルにアクセスしようとすると、クラッシュします。前述のように、リスト ビューがスクロールされた場合にのみクラッシュします。なぜそれが影響するのかわかりません。

クラッシュを引き起こさずにカーソルを正しく閉じるにはどうすればよいですか?

カーソルがどのようにロードされたかを確認するように求めるコメントに対応する EDIT コード:

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);
        }
4

2 に答える 2

0

非推奨の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();
   }
}
于 2015-10-01T09:49:08.490 に答える
-1

これで試してください

        Cursor cursor = null;
        try {
            cursor = (Cursor) getListView().getItemAtPosition(n);
            //do something

        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
于 2015-09-15T14:11:48.323 に答える