NavBackStackEntryのLifecycleについて

NavController が持ってる NavBackStackEntry ですが、実はこれも Lifecycle を持っていてFragment自身のLifecycleやViewのLifecycleと異なるので、それについて解説したいと思います。

今回の検証には Navigationの 2.3.0-alpha04 を使っています。

また、Navigation Component自体の解説はしません。

検証用のコード

以下のように viewLifecycleOwnerNavBackStackEntry の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

こちらも同様に NavBackStackEntryViewLifecycle で異なります。

DialogFragmentの遷移

次にNavigationを使ってDialogFragmentに遷移した場合です。

Logcatには以下のように表示されます。

[NavBackStackEntry] ON_PAUSE

NavBackStackEntryON_PAUSE になりますが、 ViewLifecycle については変更なしです。

今度はDialogが閉じたときです。

[NavBackStackEntry] ON_RESUME

こちらも遷移時と同じように ViewLifecycle は変更なしで、 NavBackStackEntry のみが ON_RESUME になります。

いつ使うのか?

これは currentBackStackEntrypreviousBackStackEntry を使ったDialogFragmentの戻り値を取得する際に必要になります。

currentBackStackEntrypreviousBackStackEntry を使った戻り値を扱う方法は以前書いた記事を参考にしてください。

問題点

結果を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 でやったほうが良いと思います。

(前回の記事で指摘を受けたので改めて調査して書きました。指摘ありがとうございました。)

--

--

Programmer / Gamer / Google Developers Expert for Android, Kotlin / @STAR_ZERO

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store