ライフサイクルとコルーチン
Actor は、UI管理にも便利で、タスクのキャンセルをシンプルにし、UIスレッドのオーバーロードを避けることができます。
まず、Activity に適用する JobHolder インターフェースを作成します。これは、セットしたタスクの親となり、それのキャンセルを可能にします。
interface JobHolder {
val job: Job
}
Activity が destroy されるときに、job.cancel() を行います。
class MyActivity : AppCompatActivity(), JobHolder {
override val job: Job = Job() // the instance of a Job for this activity
override fun onDestroy() {
super.onDestroy()
job.cancel() // cancel the job when activity is destroyed
}
}
Extension Function にして、JobHolder の すべての View からアクセス可能にします。
val View.contextJob: Job
get() = (context as? JobHolder)?.job ?: NonCancellable
これらを組み合わせて、setOnClick に onClick のアクションを管理させるための conflated な Actor を作らせます。複数回の連続クリックは無視され、ANR を避けることができます。
そして、これらのアクションは、contextJob のコンテキストで実行されます。
また、Activity が destroy されるとキャンセルもされます。
fun View.setOnClick(action: suspend () -> Unit) {
val eventActor = actor<Unit>(
context = UI,
start = CoroutineStart.UNDISPATCHED,
capacity = Channel.CONFLATED,
parent = contextJob
) {
for (event in channel) action()
}
setOnClickListener { eventActor.offer(Unit) }
}
この例では、ここでは多すぎるイベントを無視するために Channel を conflated としてセットしています。すべてをイベントキューとしたい場合は、Channel.UNLIMITED とすることができます。その場合でも ANR は発生しません。
コルーチンとライフサイクルを組み合わせて、UIタスクのキャンセルを自動化することもできます。
val LifecycleOwner.untilDestroy: Job get() {
val job = Job()
lifecycle.addObserver(object: LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() { job.cancel() }
})
return job
}
// 使い方
launch(UI, parent = untilDestroy) {
// 何らかの処理
}
【Android】Kotlin でモダンな concurrency その1
【Android】Kotlin でモダンな concurrency その2
【Android】Kotlin でモダンな concurrency その3
【Android】Kotlin でモダンな concurrency その4