Android アプリに LifecycleObserver を導入した話
はじめに
ドワンゴ社の N予備校 Android アプリ開発チームに業務委託として参加している藤崎です。私達のアプリに LifecycleObserver を導入した話を紹介します。
公式のベストプラクティス:ライフサイクルメソッドのオーバーライドをしない
Google の公式ドキュメント(以下、公式)では「強く推奨」「推奨」「省略可」の3段階の優先度を用いて、Android アーキテクチャに関する推奨事項を列挙しています。ベストプラクティスと推奨事項はほぼ同義語として使われています。
その中で、 onResume などのライフサイクルメソッドをオーバーライドしないことを「強く推奨」しています。
強く推奨: アクティビティやフラグメントのライフサイクルメソッドをオーバーライドしないようにします。
優先度が「強く推奨」であることに加え、一般に公式の推奨事項は他社でも従っている可能性が高く、公式の推奨に従ったコードはチームの新メンバーにとっても馴染みやすい可能性が高いことから、この推奨に従うことにしました。
onViewCreated 以前のライフサイクルメソッドのオーバーライドは残す
先述のサイトでは LifecycleObserver を導入したスニペットを紹介しています。そこでは
onViewCreated はオーバーライドしたままのため、私達のアプリでも onViewCreated 以前のライフサイクルメソッドはオーバーライドし続けることにしました。
LifecycleObserver の導入
はじめに
先述のサイトでは View 用のスニペットと Compose 用のスニペットを両方紹介しています。私達のアプリは大部分が Compose に移行済みですが、Compose 内に LifecycleObserver を導入するにはアプリの既存設計を大きく修正する必要があるため、今回は View 用のスニペットにならって Compose を導入します。
Activity に対する LifecycleObserver の導入
onCreate で DefaultLifecycleObserver を追加します。
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {}
override fun onResume(owner: LifecycleOwner) {}
override fun onPause(owner: LifecycleOwner) {}
override fun onStop(owner: LifecycleOwner) {}
override fun onDestroy(owner: LifecycleOwner) {}
}
)
}
}
DialogFragment に対する LifecycleObserver の導入
onCreateDialog で DefaultLifecycleObserver を追加します。
class MyDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {}
override fun onResume(owner: LifecycleOwner) {}
override fun onPause(owner: LifecycleOwner) {}
override fun onStop(owner: LifecycleOwner) {}
override fun onDestroy(owner: LifecycleOwner) {}
}
)
}
}
DialogFragment 以外の Fragment に対する LifecycleObserver の導入
onCreate と onViewCreated で DefaultLifecycleObserver を追加します。
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { {
lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
/* Fragment.onDestroy() に相当する。 */
}
}
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {}
override fun onResume(owner: LifecycleOwner) {}
override fun onPause(owner: LifecycleOwner) {}
override fun onStop(owner: LifecycleOwner) {}
override fun onDestroy(owner: LifecycleOwner) {
/* Fragment.onDestroyView() に相当する。 */
}
}
)
}
}
上記の補足説明は以下の通りです。
-
viewLifecycleOwner.lifecycle.addObserver
内におけるonDestroy
はFragment.onDestroyView
に相当する。 -
lifecycle.addObserver
内におけるonDestroy
はFragment.onDestroy
に相当する。 -
viewLifecycleOwner.addObserver
をonViewCreated
で呼ぶ理由は、onCreatedView
以前に参照するとエラーになるため。 -
lifecycle.addObserver
をonCreate
で呼ぶ理由は、onCreateView
以降で呼ぶとFragment.view
が再生成される度に不必要に呼ばれてしまうため。
LifecycleObserver の導入後1ヶ月経って
今回の対応の動機の一つであるチームの新メンバーの馴染みやすさについては、まだ新メンバーが加入していないため、検証する機会はまだありません。
一方で、NowInAndroid など今回の推奨事項に従っているオープンソースのアプリもあるため、オープンソースのコードに僅かながら馴染みやすくなる利点もありそうです。
おわりに
今回は LifecycleObserver を導入した話を紹介しました。公式の推奨事項になるべく沿ったアプリを設計することは、チーム開発において重要だと思っています。今回の話が LifecycleObserver の導入に興味がある方々の参考になれば幸いです。
Discussion