Fragment
を取り扱っていると必ず出てくるのが、FragmentManager
とFragmentTransaction
です。
FragmentManager
は、1 個のActivity
につき 1 個のFragmentManager
が居て、Activity
のライフサイクルの巡りが尽きて死ぬまでの間、そのActivity
でのFragment
のライフサイクル管理をしてくれます。
Fragment
の取り扱い方としては、レイアウトに埋め込んで静的に動作させるやり方と、Activity
が動的にFragment
のインスタンスを生成してレイアウトにアタッチするやり方があり、特に後者の動的にアタッチするやり方を採用した時に、FragmentTransaction
を用いてFragment
のレイアウトに対する各種の操作を実行します。
さて、FragmentTransaction
を使って動的にアタッチしようとすると、たまに以下の様な事象に出くわすことが有ります。
イベントのコールバック中に FragmentManager#findFragmentByTag() でアタッチした Fragment を取得しようとしたら、 FragmentTransaction#commit() したはずなのに null が返ってきた
FragmentTransaction#commit()
が、即座にFragment
をレイアウトに反映するわけではない、ということは、Android Developers にも記載があります。
Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.
トランザクションのコミットをスケジューリングするよ、ということなので、commit()
メソッドが同期的にFragment
をアタッチするわけではない、というわけです。もう少し掘り下げると、できるだけ早く、メインスレッドが有効になってから、commit()
された内容を実行できるように、Handler
にメッセージを投げてスケジューリングする、という事になります。
このような状況の回避策としてあげられるのが、FragmentManager#executePendingTransactions()
です。
このメソッドの実体はこのあたりにあります。
ざっくりとまとめると、FragmentTransaction#commit()
によってキューに積まれたジョブを同期的に実行し、Fragment
のアタッチ等をその場で実行してくれる、ということをします。
ということは、メインスレッドが有効になってからできるだけ早く実行されるとはいえ、メッセージキューに積まれたメッセージの数に依存して、実際の実行されるタイミングのスケジュールが決まるということになります。
つまり、FragmentTransaction
によるメッセージ以外に何らかのメッセージがたまった状態だと、FragmentTransaction
による操作が実行される前に他のメッセージが処理される事になり、そのメッセージの中でFragmentManager#findFragmentByTag()
等をすると、まだFragment
がアタッチされていないのでnull
が返る、ということになります。
ありがちなのは、非同期処理のコールバックでプログラスを表示するFragment
を非表示にしようとしたとき、プログレスを表示するFragment
がアタッチされるよりも前に非同期処理が終わってしまい、先にメッセージキューに積まれてしまう、という状況です。
そのような状況にならないよう、FragmentManager#executePendingTransactions()
を使って、先にFragment
をアタッチしてから非同期処理を開始するように強制する、ということです。
もちろん、Fragment
をアタッチする操作が先になるよう強制されるので、アタッチされたことを受けて何らかの振る舞いをするような場合には、注意が必要です。これはドキュメントにも記載されています。
Note that all callbacks and other related behavior will be done from within this call, so be careful about where this is called from.
もう、これでFragmentManager#executePendingTransactions()
は怖くない!