NavController
が持ってる NavBackStackEntry
ですが、実はこれも Lifecycle
を持っていてFragment自身のLifecycleやViewのLifecycleと異なるので、それについて解説したいと思います。
今回の検証には Navigationの 2.3.0-alpha04
を使っています。
また、Navigation Component自体の解説はしません。
検証用のコード
以下のように viewLifecycleOwner
と NavBackStackEntry
のlifecycleにObserverを設定してすべてのイベントをLogcatに表示していき、違いを見て行きます。
Fragmentの遷移
Navigationを使って別のFragmentに遷移した場合にどのようなイベントになるかを確認してみます。
Navigationを使って遷移させるようにします。
// 次のFragmentへ
findNavController().navigate(R.id.action_next)
ログは以下のようになります。
[NavBackStackEntry] ON_PAUSE
[NavBackStackEntry] ON_STOP
[ViewLifecycle] ON_PAUSE
[ViewLifecycle] ON_STOP
[ViewLifecycle] ON_DESTROY
NavBackStackEntry
のほうは ON_STOP
になっていますが、 ViewLifecycle
のほうは ON_DESTROY
まで実行されています。
今度は遷移後のFragmentからバックキー等で前のFragmentに戻った場合です。
[NavBackStackEntry] ON_START
[NavBackStackEntry] ON_RESUME
[ViewLifecycle] ON_CREATE
[ViewLifecycle] ON_START
[ViewLifecycle] ON_RESUME
こちらも同様に NavBackStackEntry
と ViewLifecycle
で異なります。
DialogFragmentの遷移
次にNavigationを使ってDialogFragmentに遷移した場合です。
Logcatには以下のように表示されます。
[NavBackStackEntry] ON_PAUSE
NavBackStackEntry
は ON_PAUSE
になりますが、 ViewLifecycle
については変更なしです。
今度はDialogが閉じたときです。
[NavBackStackEntry] ON_RESUME
こちらも遷移時と同じように ViewLifecycle
は変更なしで、 NavBackStackEntry
のみが ON_RESUME
になります。
いつ使うのか?
これは currentBackStackEntry
と previousBackStackEntry
を使ったDialogFragmentの戻り値を取得する際に必要になります。
currentBackStackEntry
と previousBackStackEntry
を使った戻り値を扱う方法は以前書いた記事を参考にしてください。
問題点
結果を1度しか受け取りたくない場合(画面回転等で再取得しないようにする)は、結果を受け取ったら以下のように savedStateHandle#remove
を呼び出してクリアして上げる必要があります。
通常のFragmentの場合はこれで問題ありません。
しかしDialogFragmentの場合は、ダイアログを再度表示して結果を受け取ろうとした場合も受け取ることができなくなります。
savedStateHandle#remove
がLiveDataも削除してしまうので、次回からはLiveDataに通知されなくなるためです。
Fragmentの遷移であれば戻ったときに onViewCreated
等のイベントが再び実行され savedStateHandle#getLiveData
が実行されてから結果を受け取るので問題ありません。
対応方法
では、どのように対応するのかです。この対応するときにようやく NavBackStackEntry
のLifecycleを使います。
以下のような実装になります。
NavBackStackEntry
のLifecycleが ON_RESUME
で、 savedStateHandle
に結果が入ってる場合のみに結果を取得するようにします。
また、このときはLiveDataとしてではなく、直接 get
で取得するようにします。
こうすることでDialogFragmentも問題なく動作することができます。
参考
これまで書いてきたことはドキュメントにも記載されてるので目を通しておくと良いと思います。
ドキュメントだと onCreate
でやってますが、回転したときにクラッシュするので onViewCreated
でやったほうが良いと思います。
(前回の記事で指摘を受けたので改めて調査して書きました。指摘ありがとうございました。)