👀

repeatOnLifecycleとlaunchWhenXX

2022/02/27に公開

ActivityやFragmentでFlowをcollectする際にrepeatOnLifecyclelaunchWhenResumeなどを利用しているコードがよくあると思います。

この2つのメソッドの違いと使い所を見ていきます。

注意

後述もしますがlaunchWhenXXは削除予定とされていて、挙動としてもFlowをcollectするには向かないためrepeatOnLifecycleを利用することをオススメします。

https://developer.android.com/reference/kotlin/androidx/lifecycle/LifecycleCoroutineScope#launchWhenResumed(kotlin.coroutines.SuspendFunction1)

特徴

launchWhenXX

launchWhenXXには以下の3種類があります。

  • launchWhenCreated
  • launchWhenResumed
  • launchWhenStarted

これらのメソッドの挙動としては

  • 引数で渡している処理の実行を指定したライフサイクルになるまで延期
  • 指定したライフサイクルになったタイミングで処理が開始され、その指定されたライフサイクルに対応したライフサイクルになると処理が中断
  • 再度指定したライフサイクルになった場合に処理が再開

となっています。

例としてlaunchWhenStartedの場合

  1. Activity/FragmentがSTARTEDになると処理を開始
  2. Activity/FragmentがSTOPEDになると処理を中断
  3. 再度Activity/FragmentがSTARTEDになると処理を再開

といった流れになります。

同じ挙動を行うものとしてLifecycleOwner?.whenXXといったメソッドがあります。
こちらもwhenCreated/whenResumed/whenStartedといった3種類あります。

またlaunchWhenXXのメソッドは削除予定とされていて、whenXXに関しては何も言及されていないため、Flowをcollectする以外で同様の挙動を利用したい場合は代わりにこちらを使うと良さそうです。

repeatOnLifecycle

repeatOnLifecycleは種類があるわけではなく、repeatOnLifecycle(Lifecycle.STARTED)のようにすることで処理を開始するライフサイクルを指定することができます。
repeatOnLifecyclelaunchWhenXXと同様に指定したライフサイクルで処理を開始したり特定のライフサイクルで止めたりするものです。

この記事を書くために調べるまで知らなかったのですが、lifecycle-runtime-ktx:2.4.0-alpha01で導入されたflowWithLifecycleというものがあります。
こちらもLifecycle.Stateを渡すことでrepeatOnLifecycleと同様な挙動を実現できそうです。
https://developer.android.com/reference/kotlin/androidx/lifecycle/package-summary#(kotlinx.coroutines.flow.Flow).flowWithLifecycle(androidx.lifecycle.Lifecycle,androidx.lifecycle.Lifecycle.State)

flowWithLifecycleはインデントを減らせますが、複数のFlowをcollectする場合はrepeatOnLifecycleの方がまとめやすいためケースや開発方針でどちらを利用するか選択すると良さそうです。

違い

launchWhenXXrepeatOnLifecycleの違いとして、特定のライフサイクルになった時の停止・再開の挙動が異なります。
launchWhenXXSTOPEDなどの特定のライフサイクルになった場合は実行中の処理を中断、再度STARTEDなどになった時はその処理を再開します。
それに対し、repeatOnLifecycleSTOPEDなどの特定ライフサイクル時に処理をキャンセル、再度STARTEDなどになった時は処理を最初から実行し直します。

この違いからFlowのcollectする際はrepeatOnLifecycleを利用することが公式からも推奨されています。

ただ、repeatOnLifecycleは指定したライフサイクルになるたびに処理を最初から実行するため、特定のライフサイクル時に一度だけ処理を実行したいといったケースのには向いていないです。
そういった際は、LifecycleOwner?.whenXXを利用すると良さそうです。

もう少し深掘り

では、なぜrepeatOnLifecycleの方が良いのかというと、launchWhenXXではSTOPEDになった場合に処理が中断されSTARTEDになった際に再開されるためcoroutineの処理が残り続けることになります。
このようにcoroutineが残っていることによって、STOPED〜STARTEDの間のバックグラウンドにある状態でもFlowがアクティブのままになり新しいアイテムが流れる可能性があったりリソースの浪費に繋がってしまったりします。
そのため、Flowをcollectする際はrepeatOnLifecycleの利用が推奨されています。

まとめ

まとめとしては以下のような使い分けになります。
Flowをcollectしたい! → repeatOnLifecycle
一度きり(ショットな)suspend処理を実行したい! → whenXX

参考

https://developer.android.com/topic/libraries/architecture/coroutines#suspend
https://zenn.dev/verno3632/articles/4a98da03dcdbaa#flowwithlifecycle
https://at-sushi.work/blog/35/

Discussion