Composable のライフサイクルと Android(Activity/Fragment) のライフサイクルを分けて考える

フル Compose でアプリを作っていると、LifecycleEventObserver を使って画面のライフサイクルを監視したくなることがあります。
たしかに動きます。
しかし、そのコードが本当に必要としているのは Activity/Fragment のライフサイクルなのか、それとも Composable のライフサイクル なのか を一度考えてみるべきです。
この2つは似ていますが、別のものです。
🧑🏻💻 ComposableのLifecycle と Android Lifecycle は別物
Composable には、Composition に入る・再コンポーズされる・Composition から離れる、というライフサイクルがあります。
一方、Android には Activity や Fragment などの LifecycleOwner が持つライフサイクルがあります。
Composable
│
├─ enters Composition
├─ recomposes
└─ leaves Composition
Activity / Fragment
│
├─ ON_CREATE
├─ ON_START
├─ ON_RESUME
├─ ON_PAUSE
├─ ON_STOP
└─ ON_DESTROY
フル Compose だからといって Android ライフサイクルがなくなるわけではありません。
Composable のライフサイクルを扱うために Android ライフサイクルを使う必要はありません。
🧑🏻💻 LifecycleEventObserver がコードを複雑にする理由
例えば、画面が Composition に入ったときに何かを登録し、画面から離れたら解除したいとします。
Android ライフサイクルを使うと、こう書けます。
よくある書き方:
@Composable
fun MyScreen() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_START -> {
// start
}
Lifecycle.Event.ON_STOP -> {
// stop
}
else -> Unit
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
もちろん正しく動きます。
でも、ここで必要なのが単純に
「この Composable が存在している間だけ登録したい」
ということなら、Android ライフサイクルまで持ち込む必要はありません。
Composable のライフサイクルに合わせる:
@Composable
fun MyScreen() {
DisposableEffect(Unit) {
// start
onDispose {
// stop
}
}
}
こちらのほうが、コードから意図が直接読み取れます。
🧑🏻💻 Composable のライフサイクルなら DisposableEffect
DisposableEffect は、Composable が Compositionに 入ったときに処理を開始し、Composition から離れたときに後始末できます。
例えばリスナーの登録です。
Before:
@Composable
fun MyScreen() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
startListening()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
stopListening()
}
}
}
After:
@Composable
fun MyScreen() {
DisposableEffect(Unit) {
startListening()
onDispose {
stopListening()
}
}
}
「Composable が存在する間だけ必要なリソース」というだけなら、これで十分です。
🧑🏻💻 Coroutine なら LaunchedEffect
Coroutine を Composable のライフサイクルに合わせたい場合も、LifecycleObserver を使う必要はありません。
Before:
@Composable
fun MyScreen() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
// coroutineを開始
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
After:
@Composable
fun MyScreen() {
LaunchedEffect(Unit) {
// coroutine
}
}
LaunchedEffect の Coroutine は、その Effect が Composition から外れると自動的にキャンセルされます。
つまり、
Composable
│
└── LaunchedEffect
│
└── Coroutine
という関係です。
🧑🏻💻 FlowならcollectAsStateWithLifecycle()
Flow を UI で表示するだけなら、LifecycleObserver を自分で書く必要はありません。
Before:
@Composable
fun MyScreen(viewModel: MyViewModel) {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
// Flowをcollect
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
After:
@Composable
fun MyScreen(viewModel: MyViewModel) {
val uiState by viewModel.uiState
.collectAsStateWithLifecycle()
Content(uiState)
}
Flow の Lifecycle-aware な収集は、専用の API に任せます。
自分でライフサイクルを監視して Flow を開始・停止するより、意図がはっきりします。
🧑🏻💻 Lifecycle イベントが必要なら LifecycleEventEffect
ここまでとは違い、本当に Android ライフサイクル のイベントそのものが必要なケースもあります。
例えば ON_RESUME や ON_PAUSE を明示的に扱いたい場合です。
その場合は、LifecycleObserver を自分で組み立てるより、ライフサイクルイベント用の API を使えます。
Before:
@Composable
fun MyScreen() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
refresh()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
After:
@Composable
fun MyScreen() {
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
refresh()
}
}
ここではじめて、Android ライフサイクル を使う理由が明確になります。
「Composable が存在する間」ではなく、「Android ライフサイクル の ON_RESUME が発生したとき」が必要だからです。
🤔 LifecycleEventObserver が必要になるケース
だからといって、LifecycleEventObserver が悪いわけではありません。
次のようなケースでは、直接使う意味があります。
・ 複数のライフサイクルイベントをまとめて監視したい
・ 独自のライフサイクル連携を実装している
・ Lifecycle-aware なライブラリやAPIと統合する
・ LifecycleEventEffect では表現しにくい処理がある
重要なのは、
LifecycleEventObserver を使うな、ではなく、必要のないところに持ち込まない
ということです。
🧑🏻💻 8. まとめ
迷ったら、まず「この処理はどの寿命に属しているのか」を考えます。
・ Composable のライフサイクル → DisposableEffect
・ Composable に紐づく Coroutine → LaunchedEffect
・ Flow を UIで収集 → collectAsStateWithLifecycle()
・ Activity/Fragment のライフサイクルイベント → LifecycleEventEffect
・ 複雑なライフサイクル連携 → LifecycleEventObserver
つまり、
何のライフサイクル・イベントを扱っている?
│
├─ Composable
│ ├─ Resource → DisposableEffect
│ └─ Coroutine → LaunchedEffect
│
├─ Flow → collectAsStateWithLifecycle()
│
└─ Android Lifecycle
├─ Simple event → LifecycleEventEffect
└─ Complex integration → LifecycleEventObserver
フル Compose では、Android ライフサイクルが使えるからといって、すぐに LifecycleEventObserver を持ち込む必要はありません。
処理が属しているライフサイクルに、一番近い API を選ぶ。
それだけで、Compose のコードはかなりシンプルになります。






