9

現在、構成の変更中に高価なデータ構造を保持したいと考えています。Bundle高価なデータ構造はパーセル化できないため、使用しないことにしました。

したがって、非 UI フラグメント ( RetainInstanceFragmentと呼ばれます) を使用setRetainInstance(true)して、データ構造を保持します。

public class RetainInstanceFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Creating expensive data structure
        expensiveDataStructure = CreateExpensiveDataStructure();

        // Tell the framework to try to keep this fragment around
        // during a configuration change.
        setRetainInstance(true);
    }

    public ExpensiveDataStructure expensiveDataStructure = null;
}

UI フラグメント ( UIFragmentと呼ばれる) は、 から高価なデータ構造を取得しRetainInstanceFragmentます。で設定が変更されるたびにUIFragment、は、新しい を作成する前に、常にからUIFragment「キャッシュ」を取得しようとします。RetainInstanceFragmentFragmentManagerRetainInstanceFragment

サンプルコードは以下の通りです。

public class UIFragment extends SherlockListFragment
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        FragmentManager fm = getFragmentManager();

        // Check to see if we have retained the worker fragment.
        retainInstanceFragment = (RetainInstanceFragment)fm.findFragmentByTag("data");

        // If not retained (or first time running), we need to create it.
        if (retainInstanceFragment == null) {
            retainInstanceFragment = new RetainInstanceFragment();
            fm.beginTransaction().add(watchlistArrayFragment, "data").commit();
        } else {
            // We can re-use retainInstanceFragment.expensiveDataStructure even
            // after configuration change.
        }
    }
}

ただし、問題があります。古いものを破棄して新しいものにUIFragment置き換えるときはいつでも、古いものも同様に破棄されることを期待しています。これが私が破壊して新しいものを作成する方法ですUIFragmentRetainInstanceFragmentUIFragment

public class MyFragmentActivity extends SlidingFragmentActivity    
    // Being triggered when there is different menu item in sliding menu being
    // selected.
    public void selectActiveContent(Country country) {
        Fragment fragment = new UIFragment(country);
        getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss();
    }

しかし、古いRetainInstanceFragmentものは決して破壊されません。

私の推測では、おそらく でクリーンアップを実行するのを忘れているのでしょうUIFragment。したがって、次のコードを追加します

UIフラグメント

@Override
public void onDetach() {
    super.onDetach();
    // To differentiate whether this is a configuration changes, or we are
    // removing away this fragment?
    if (this.isRemoving()) {
        FragmentManager fm = getFragmentManager();
        fm.beginTransaction().remove(retainInstanceFragment).commit();
    }
}

ただし、常に機能するわけではありません。スライド メニューを数回クリックします。

1. selectActiveContent() -> Create new UIFragment and new RetainInstanceFragment
2. selectActiveContent() -> Create new UIFragment, but re-use previous RetainInstanceFragment. (Wrong behavior)
3. selectActiveContent() -> Create new UIFragment, and new RetainInstanceFragment.
4. selectActiveContent() -> Create new UIFragment, but re-use previous RetainInstanceFragment. (Wrong behavior)

保持されたインスタンスフラグメントを適切に削除する方法はありますか?

4

2 に答える 2

7

@Luksprog で提案されているように、次の方法が機能します。ただし、以前に行ったクリーンアップが機能しない理由はまだ説明されてonDetachいません。このソリューションが機能し、以前のソリューションが機能しない理由を誰かが説明できれば、とても感謝しています。:)

UIフラグメント

@Override
public void onDetach() {
    super.onDetach();
}

public void cleanupRetainInstanceFragment() {
    FragmentManager fm = getFragmentManager();
    fm.beginTransaction().remove(this.retainInstanceFragment).commit();
}

マイフラグメントアクティビティ

public class MyFragmentActivity extends SlidingFragmentActivity    
    // Being triggered when there is different menu item in sliding menu being
    // selected.
    public void selectActiveContent(Country country) {

        // *******************************************
        // Solution suggested by @Luksprog. It works!
        // But I have no idea why it works and previous doesn't work...
        // *******************************************
        Fragment oldFragment = getSupportFragmentManager().findFragmentById(R.id.content);
        if (oldFragment instanceof UIFragment) {
            ((UIFragment)oldFragment).cleanupRetainInstanceFragment();
        }

        Fragment fragment = new UIFragment(country);
        getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss();
    }

(編集済み) @Luksprog による有用なコメント

フラグメント トランザクションはすぐには実行されません。私の仮定では、 onDetach() コールバックでそのトランザクションを実行しても、UI フラグメントの置換トランザクションが完了する前に保持フラグメント インスタンスが削除されないため、新しい UI フラグメントには保持フラグメント インスタンスがまだ使用可能であることが表示されるため、新しい1。以前の方法は、フラグメントが他のフラグメントを認識せず、アプリケーション全体の状態についてより多くのことを知っているため、アクティビティがそれらすべてを管理するフラグメント フレームワークの精神にはありません。

于 2013-03-22T09:28:47.583 に答える
0

フラグメントトランザクションからフラグメントを削除するだけでよいと思います。

            if (mWorkFragment != null) {
              fm.beginTransaction().remove(mWorkFragment).commitAllowingStateLoss();
            }
于 2015-05-20T07:38:02.967 に答える