DialogFragmentとAlertDialogと画面遷移

DialogFragment の内部でシンプルな AlertDialog を使っていて、さらにその AlertDialog のボタンから画面遷移の処理をしているときの注意点についてです。

簡単な例

まずは DialogFragment の実装です。
ダイアログの結果の通知に共有の ViewModel を使っていますが、 setFragmentResultListener でも同様です。

AlertDialog は通常ではボタン押下後に自動でダイアログが閉じるようになってるので、明示的にダイアログを閉じるようなことはしていません。

次に呼び出し元の Fragment の実装です。

実際の結果

ダイアログのボタンを押して、画面遷移し、そのあとにバックキー等で戻ってくると再びダイアログが表示されます。

(このgifわかりにくいですね。。。)

原因

通常の動作

DialogFragment で保持している AlertDialogsetOnDismissListener で閉じたときのイベントが実装されています。
source code )

この setOnDismissListener が起きたタイミングで、 onDismiss() が呼び出され、最終的に dismissInternal() が呼び出されます。
( source code )

この dismissInternal() で、FragmentTransaction#removeDialogFragment を削除しています。
( source code )

以上の動作から AlertDialog が正常に閉じられれば、 DialogFragmentFragmentManger から削除されることになります。

まとめると、
1. ダイアログのボタンを押す
2. ダイアログが閉じる
3. 閉じるときにダイアログの setOnDismissListener が実行される
4. FragmentManager から DialogFragment が破棄される

という感じです。

画面遷移したときの動作

まず知っておいてほしいのが、 DialogFragment が表示されていても呼び出し元のライフサイクルとしては、 ON_RESUME の状態になります。戻り値の処理を今回のような ViewModel を使った場合も setFragmentResultListener 使った場合も、 DialogFragment が閉じる前に即実行されます。

上記の理由から、DialogFragment が保持してる AlertDialog が破棄される前に画面遷移が行われていることになります。

先に、 DialogFragmentonDestroyView が呼び出され、その中で内部で持っている AlertDialogdimiss() でダイアログを閉じています。ただし、この直前で setOnDismissListener をnullにしています。
( source code )

最終的には、 dismissInternal() が呼び出されないため FragmentManger から破棄されません。

まとめると
1. ダイアログのボタンを押す
2. イベント通知で即座に画面遷移処理
3. onDestroyView でダイアログを閉じる。
4. (DialogFragment 自体は破棄されない)

そのため、画面遷移後に戻ったときに DialogFragment が破棄されていないため再表示されることになります。

この挙動は画面回転とほぼ同じ感じになります。

対応

補足

https://developer.android.com/guide/fragments/dialogs#showing

When creating a DialogFragment from within a Fragment, you must use the Fragment's child FragmentManager to ensure that the state is properly restored after configuration changes.

実は今回の紹介した問題は、 parentFragmentManager を使うと起きないです。ドキュメントに書いてる通り、 childFragmentManager を使った場合はしっかりとrestoreが動くので今回の現象に繋がります。

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

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