🛠️

Android アプリに LifecycleObserver を導入した話

2023/07/03に公開

はじめに

ドワンゴ社の N予備校 Android アプリ開発チームに業務委託として参加している藤崎です。私達のアプリに LifecycleObserver を導入した話を紹介します。

公式のベストプラクティス:ライフサイクルメソッドのオーバーライドをしない

Google の公式ドキュメント(以下、公式)では「強く推奨」「推奨」「省略可」の3段階の優先度を用いて、Android アーキテクチャに関する推奨事項を列挙しています。ベストプラクティスと推奨事項はほぼ同義語として使われています。

その中で、 onResume などのライフサイクルメソッドをオーバーライドしないことを「強く推奨」しています。

強く推奨: アクティビティやフラグメントのライフサイクルメソッドをオーバーライドしないようにします。

https://developer.android.com/topic/architecture/recommendations?hl=ja#lifecycle

優先度が「強く推奨」であることに加え、一般に公式の推奨事項は他社でも従っている可能性が高く、公式の推奨に従ったコードはチームの新メンバーにとっても馴染みやすい可能性が高いことから、この推奨に従うことにしました。

onViewCreated 以前のライフサイクルメソッドのオーバーライドは残す

先述のサイトでは LifecycleObserver を導入したスニペットを紹介しています。そこでは
onViewCreated はオーバーライドしたままのため、私達のアプリでも onViewCreated 以前のライフサイクルメソッドはオーバーライドし続けることにしました。

LifecycleObserver の導入

はじめに

先述のサイトでは View 用のスニペットと Compose 用のスニペットを両方紹介しています。私達のアプリは大部分が Compose に移行済みですが、Compose 内に LifecycleObserver を導入するにはアプリの既存設計を大きく修正する必要があるため、今回は View 用のスニペットにならって Compose を導入します。

Activity に対する LifecycleObserver の導入

onCreateDefaultLifecycleObserver を追加します。

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 の導入

onCreateDialogDefaultLifecycleObserver を追加します。

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 の導入

onCreateonViewCreatedDefaultLifecycleObserver を追加します。

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 内における onDestroyFragment.onDestroyView に相当する。
  • lifecycle.addObserver 内における onDestroyFragment.onDestroy に相当する。
  • viewLifecycleOwner.addObserveronViewCreated で呼ぶ理由は、onCreatedView 以前に参照するとエラーになるため。
  • lifecycle.addObserveronCreate で呼ぶ理由は、onCreateView 以降で呼ぶと Fragment.view が再生成される度に不必要に呼ばれてしまうため。

LifecycleObserver の導入後1ヶ月経って

今回の対応の動機の一つであるチームの新メンバーの馴染みやすさについては、まだ新メンバーが加入していないため、検証する機会はまだありません。

一方で、NowInAndroid など今回の推奨事項に従っているオープンソースのアプリもあるため、オープンソースのコードに僅かながら馴染みやすくなる利点もありそうです。

おわりに

今回は LifecycleObserver を導入した話を紹介しました。公式の推奨事項になるべく沿ったアプリを設計することは、チーム開発において重要だと思っています。今回の話が LifecycleObserver の導入に興味がある方々の参考になれば幸いです。

Discussion