1. ホーム
  2. java

[解決済み】popBackStack()とreplace()の操作はどう違うのですか?

2022-01-18 22:37:55

質問

私のアプリケーションでフラグメントを管理する際に、不思議な動作に遭遇しました。

私のアプリケーションの一般的な流れは、ユーザーが何らかの方法でフラグメントAとインタラクションを行うと、フラグメントBが fragmentTransaction.replace() (これはすべてのケースで起こる)。 フラグメントBを表示するとき、フラグメントAをバックスタックに追加します。そして、ユーザーがフラグメントBのバックボタンを押すと、フラグメントAはバックスタックからポップアップして再び表示されるのです。

それはいいのですが、今日、フラグメントBから fragmentTransaction.replace() フラグメントBは、現在バックスタックにあるフラグメントAと同じインスタンスに置き換わります。

それ自体は問題ないのですが、フラグメントAからフラグメントBに戻るときにおかしな動作が発生します。 fragmentTransaction.replace() は、その onCreate() メソッドは呼び出されません。

しかし、バックスタックからフラグメントAをポップし、フラグメントBと入れ替えると onCreate() メソッドが実行されます。 これはなぜでしょうか?

フラグメントAとフラグメントBのインスタンスは、ホストアクティビティが起動した時点ですべて生成されることに注意してください。

明確化のために編集します。 この場合 onCreate() が2回目に呼ばれるのは以下の通りです。フラグメントAをアタッチする => フラグメントBに置き換えて、フラグメントAをバックスタックに追加する => フラグメントAを popBackStack() => Fragment Aを再びFragment Bに置き換える。

どのように解決するのですか?

replace() は、2つのことを行います。

  1. 現在追加されているフラグメント(A)を、指定したコンテナ(C)から削除する。
  2. 同じコンテナに新しいフラグメント(B)を追加する

この2つの操作が、Backstackのレコード/トランザクションとして保存されるものです。なお、フラグメントAは created 状態になり、そのビューは破棄されます。

現在 popBackStack() は、BackStackに追加した最後のトランザクションを取り消します。

この場合、2ステップになります。

  1. CからBを削除する
  2. CにAを追加

この後、フラグメントBは次のようになります。 detached そして、もしあなたがそれへの参照を保持していなかったならば、それはガベージコレクションされるでしょう。

質問の最初の部分に対する答えですが、この質問には onCreate() の呼び出しは、FragmentB が created の状態になります。そして、質問の後半部分の答えは、もう少し長くなります。

まず、重要なのは、実際に Fragments をBackstackに追加します。 FragmentTransactions . つまり、「フラグメントBに置き換えて、フラグメントAをバックスタックに追加する」と考えたとき、実際にはこの操作全体をバックスタックに追加していることになります - つまり 置換 この置換は、Aを削除し、Bを追加するという2つのアクションで構成されています。

そして、次のステップは、この置換を含むトランザクションをポップすることです。つまり、FragmentAをpopしているのではなく、"remove A, add B"を反転させて、"remove B, add A"としているのです。

最後のステップでは、FragmentManagerが認識しているBは存在しないので、最後のステップでAをBに置き換えて追加すると、Bはその初期のライフサイクルメソッドを通過する必要があります。 onAttach()onCreate() .

以下のコードは、その様子を示しています。

FragmentManager fm  = getFragmentManager();
FragmentA fragmentA = new FragmentA();
FragmentB fragmentB = new FragmentB();

// 1. Show A
fm.beginTransaction()
  .add(fragmentA, R.id.container)
  .commit();

// 2. Replace A with B
// FragmentManager keeps reference to fragmentA;
// it stays attached and created; fragmentB goes 
// through lifecycle methods onAttach(), onCreate()
// and so on.
fm.beginTransaction()
  .replace(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();

// 2'. Alternative to replace() method
fm.beginTransaction()
  .remove(fragmentA)
  .add(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();

// 3. Reverse (2); Result - A is visible
// What happens:
//   1) fragmentB is removed from container, it is detached now;
//      FragmentManager doesn't keep reference to it anymore
//   2) Instance of FragmentA is placed back in the container
// Now your Backstack is empty, FragmentManager is aware only
// of FragmentA instance
fm.popBackStack();

// 4. Show B
// Since fragmentB was detached, it goes through its early
// lifecycle methods: onAttach() and onCreate().
fm.beginTransaction()
  .replace(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();