Coroutine + AutoDisposeを作ってみた
Created at Sun, Dec 23, 2018Coroutine + AutoDisposeの実装について考えてみました。結論から言うと、ContinuationInterceptor
を使えば上手くいきそうです。
ContinuationInterceptorとは?
ContinuationInterceptorは次のようなインターフェースです。
/**
* Marks coroutine context element that intercepts coroutine continuations.
* The coroutines framework uses [ContinuationInterceptor.Key] to retrieve the interceptor and
* intercepts all coroutine continuations with [interceptContinuation] invocations.
*/
@SinceKotlin("1.3")
public interface ContinuationInterceptor : CoroutineContext.Element {
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
public fun releaseInterceptedContinuation(continuation: Continuation<*>)
...
}
interceptContinuation
からContinuationを受け取ることができ、Continuationは自身のCoroutineContextを持っているので、そこからJobを取得することが出来ます。それを利用することでAndroid Lifecycleと協調して動くContinuationInterceptorを実装することが出来ます。
class LifecycleContinuationInterceptor(
private val lifecycle: Lifecycle
) : ContinuationInterceptor {
override val key: CoroutineContext.Key<*>
get() = ContinuationInterceptor
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
// ContinuationからJobを取得
val job = continuation.context[Job]
if (job != null) {
lifecycle.addJob(job)
}
return continuation
}
}
fun LifecycleOwner.addJob(job: Job) {
lifecycle.addJob(job)
}
fun Lifecycle.addJob(job: Job) {
val state = this.currentState
val event = when (state) {
...
}
val observer = LifecycleJobObserver(job, event, this)
this.addObserver(observer)
job.invokeOnCompletion(observer)
}
private class LifecycleJobObserver(
private val job: Job,
private val event: Lifecycle.Event,
private val lifecycle: Lifecycle
) : LifecycleObserver, CompletionHandler {
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
fun onEvent(owner: LifecycleOwner, event: Lifecycle.Event) {
if (event == this.event) {
owner.lifecycle.removeObserver(this)
job.cancel()
}
}
override fun invoke(cause: Throwable?) {
lifecycle.removeObserver(this)
}
}
これで、Android Lifecycleと協調して動くContinuationInterceptorが出来ました。
フルコードはここにあります。
使い方
使い方は次のようになります。
abstract class BaseActivity : AppCompatActivity(),
CoroutineScope {
private val job = Job()
override val coroutineContext get() = job +
Dispatchers.Main +
LifecycleContinuationInterceptor(this) // ここでInterceptorを登録
}
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// onCreateでlaunchしているので、onDestroyで自動的にキャンセルされる
launch {
...
}
}
override fun onResume() {
super.onResume()
// onResumeでlaunchしているので、onPauseで自動的にキャンセルされる
launch {
...
}
}
}
となります。Rx-AutoDisposeのように実行したタイミングに応じて、キャンセルする場所を自動的に登録してくれます!!
まとめ
- もっと良い書き方が出来るか模索しているので、より適したAPI等を知っている人がいれば教えてくれると嬉しいです😊😊😊