Android + Kotlin Coroutine + Android Architecture Componentsで非同期

[Android Architecture Components](https://developer.android.com/topic/libraries/architecture/)の[Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle)と[Kotlin-coroutines](https://kotlinlang.org/docs/reference/coroutines.html)でライフサイクルとバインドした非同期処理を簡単に書けるようにしてみた話

Androidでの非同期といえばAsyncTaskとかRxJava+RxAndroidとかあります。
AsyncTaskだと非同期処理中にActivity等のライフサイクルが終わったときの処理を書くのが非常に面倒ですから、どちらを使うかと言えば後者なのですが、後者でもFlowableとか使うときはとてもいいのですがSingleを使うとなると少々冗長になりがちだと思います。
さらにどちらにしても`onDestory()`で`Disposable#dispose()`を呼ぶ等しなければいけませんし書き忘れがちです。
それに、async/awaitのように簡潔に書けるわけでは無いです。
なのでkotlin-coroutineのasync/awaitとAndroid Architecture ComponentsのLifecycleを用いて簡潔にかつ安全に非同期処理を書けるようにしていきたいと思います。

~*追記*~
「非同期処理周り」の`_Coroutine.kt`で定義されている関数`bindLaunch`にてミスがあったため修正しました。
稀にJob内の非同期処理が終わるタイミングによって、`onDestoroy`が呼ばれても`Job`がキャンセルされないといったミスです。

またkotlinx.coroutineの`23.0`から`CoroutineScope.coroutineContext`が非推奨になっているのでそれに合わせた変更もしました。

## 非同期処理のレスポンス
非同期処理をするならば、そのレスポンスで成功か失敗かを判断できるようにしたいところです。
なのでまずは非同期処理のレスポンスを格納するクラスを作成します。

```Response.kt
//非同期処理のResponse
sealed class Response<R>

//成功時のResponse
data class Success<R>(val result: R) : Response<R>()

//失敗時のResponse
data class Failure<R>(val error: Throwable) : Response<R>()
```

`Response`を`sealed class`にすることで継承を制限し、`when`式などで型チェックを行うことで成功時と失敗時のそれぞれの値を取り出すことができるようにしてみました。
与えた型変数は`Success`でしか使いませんが、ここは`Response`をダウンキャストしたときの利便性重視です。
値を取り出す際は例えば

```sample.kt
when (response) {
    is Success -> {
        //成功時の処理
    }
    is Failure -> {
        //失敗時の処理
    }
}
```

というふうにすればそれぞれの枝で`Response`型のオブジェクトがスマートキャストされて値が取り出せるようになります。
いちいち`when`や`if`等を用いてキャストするのが面倒なのであれば、何かしらの関数を定義してやればいいと思います

```_Resonse.kt
//Successにセーフキャスト
fun <R> Response<R>.success(): Success<R>? = this as? Success

//Failureにセーフキャスト
fun <R> Response<R>.failure(): Failure<R>? = this as? Failure

//Successな時に関数オブジェクト実行
inline fun <R> Response<R>.successIf(handle: (R) -> Unit) {
    val result = success()?.result ?: return
    handle(result)
}

//Failureな時に関数オブジェクト実行
inline fun <R> Response<R>.failureIf(handle: (Throwable) -> Unit) {
    val throwable = failure()?.error ?: return
    handle(throwable)
}

//Success、もしくはresultがnullじゃないときはその値を返して、そうでなければdefaultの値を生成して返す
fun <R> Response<R>.getOrElse(default: () -> R): R = success()?.result ?: default()

//resultを他の型や値に変換して返す
inline fun <R, T> Success<R>.map(transform: (R) -> T): T = transform(result)
```
こんな感じで

## 非同期処理周り

非同期処理の結果を格納するクラスが出来たところで、肝心の`async/await`をLifecycleとバインドさせる仕組みを作っていきたいと思います。
kotlinのasync/awaitは`launch()`などの関数でコルーチンを起動して使う感じです。
なのでその起動したコルーチンのキャンセルをLifecycleの`onDestroy()`のタイミングで行うことで、非同期処理中にActivityが死んで、コールバックの際にNPEが発生して死んでしまう。ということを防げるはず。

```_Coroutine.kt
import android.arch.lifecycle.*
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.NonCancellable.invokeOnCompletion
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.intrinsics.startCoroutineCancellable
import java.lang.ref.WeakReference
import kotlin.coroutines.experimental.CoroutineContext
import kotlin.coroutines.experimental.coroutineContext
import kotlin.reflect.KClass

//いちいちDeferred<Response<R>>って書くのめんどくさい
typealias DeferredResponse<R> = Deferred<Response<R>>

//与えられた関数オブジェクトを実行してResponseとして返す
internal inline fun <R> generateResponse(vararg expected: KClass<out Throwable> = arrayOf(Throwable::class),
                                         block: () -> R): Response<R> = try {
    Success(block())
} catch (t: Throwable) {
    if (expected.any { it.isInstance(t) })
        Failure(t)
    else throw t
}


//coroutineのasyncをResponseを返せるように拡張する感じ
fun <R> asyncResponse(context: CoroutineContext,
                      start: CoroutineStart = CoroutineStart.DEFAULT,
                      parent: Job? = null,
                      vararg expected: KClass<out Throwable> = arrayOf(Throwable::class),
                      block: suspend () -> R): DeferredResponse<R> =
        async(context, start, parent) { generateResponse(*expected) { block() } }


//以下、JobのLifecycleとのバインド周り

//Jobを保持してonDestroyでそれをキャンセルするLifecycleObserverを生成する
private fun createLifecycleObserver(job: Job) = object : LifecycleObserver {
    val mJobRef = WeakReference<Job>(job)
    
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        val j = mJobRef.get() ?: return
        val completed = j.isCompleted
        if (!completed) {
            j.cancel()
        }
    }
}

//これだと稀にLifecycleObserverをLifecycleOwnerに登録する前に、
//Job内の非同期処理が終了してしまって駄目なことがります。
//LifecycleにバインドされたJobを起動する
//fun bindLaunch(owner: LifecycleOwner, context: CoroutineContext = UI,
//               start: CoroutineStart = CoroutineStart.DEFAULT,
//               parent: Job? = null,
//               block: suspend CoroutineScope.() -> Unit
//) = launch(context, start, parent, block).apply {
//    val observer = createLifecycleObserver(this)
//    val lifecycle = owner.lifecycle
//    lifecycle.addObserver(observer)
//    //コルーチンが終わればObserverをremove()
//    invokeOnCompletion { lifecycle.removeObserver(observer) }
//}


//修正後のbindLaunch↓

//Jobを生成する関数
private fun createLazyCoroutine(context: CoroutineContext,
                        block: suspend CoroutineScope.() -> Unit
) = object : AbstractCoroutine<Unit>(context, false) {
    override fun onStart() {
        block.startCoroutineCancellable(this, this)
    }
}

private fun createCoroutine(context: CoroutineContext) =
        object : AbstractCoroutine<Unit>(context, true) {}

//修正したbindLaunch
//Jobを生成後にJobを元にして生成したLifecycleObserverを、
//LifecycleOwner登録してからJobを実行するように修正
fun bindLaunch(owner: LifecycleOwner, context: CoroutineContext = UI,
               start: CoroutineStart = CoroutineStart.DEFAULT,
               parent: Job? = null,
               block: suspend CoroutineScope.() -> Unit): Job {
    
    val newContext = newCoroutineContext(context, parent)

    val coroutine = if (start.isLazy)
        createLazyCoroutine(newContext, block) else
        createCoroutine(newContext)

    val observer = createLifecycleObserver(coroutine)
    val lifecycle = owner.lifecycle
    lifecycle.addObserver(observer)
    invokeOnCompletion { lifecycle.removeObserver(observer) }
    coroutine.start(start, coroutine, block)
    return coroutine
}

//修正後のbindLaunch↑

//CoroutineScope.coroutineContextは非推奨
//val CoroutineScope.defaultContext: CoroutineContext
//    get() = coroutineContext + CommonPool

//async()によく渡すであろうCoroutineContext
suspend inline fun defaultContext() = coroutineContext + CommonPool
```

`_Coroutine.kt`とは別のパッケージにLifecycleOwnerから呼び出せる`bindLaunch()`を定義

```_LifecycleOwner.kt
import android.arch.lifecycle.LifecycleOwner
import kotlinx.coroutines.experimental.CoroutineScope
import kotlinx.coroutines.experimental.CoroutineStart
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import tech.ketc.anktfw.androidarch.croutine.bindLaunch
import kotlin.coroutines.experimental.CoroutineContext

//bindLaunchをLifecycleOwnerの拡張関数として定義したやつ
fun LifecycleOwner.bindLaunch(context: CoroutineContext = UI, 
                              start: CoroutineStart = CoroutineStart.DEFAULT,
                              parent: Job? = null,
                              block: suspend CoroutineScope.() -> Unit) = bindLaunch(this, context, start, parent, block)

```

`bindLaunch()`で起動したコルーチンの中で`asyncResponse()`を呼び出すことで`DeferredResponse`を得られそこからawait()を呼び出せば処理を開始して終了するまでコルーチンを中断し、終了すれば`Response`を返す、と言ったことができるようになります。
例としては

```SampleActivity.kt
class AsyncSampleActivity : AppCompatActivity() {
    
    //onCreate()とかいろいろ
    ....
    
    private val mImageDownloadDispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher()
    private suspend inline fun imageDownloadContext() = coroutineContext + mImageDownloadDispatcher 

    private suspend fun downloadImage(url: String): DeferredResponse<Bitmap> = asyncResponse(imageDownloadContext()) {
        //画像をダウンロードして落としてくる処理
    }

    private fun startImageDownload() = bindLaunch {
        val response = downloadImage("http://hoge.com/hoge.jpg").await()
        response.successIf { 
            //ImageViewにセットするとか
        }
    }
}

```
Activityの中に画像のダウンロード処理みたいなロジックを書くのはあれですけど、あくまでサンプルです。
どうでしょうか？ `onDestroy()`が呼ばれた際にコルーチンをキャンセルするということを意識せずに簡潔に書けるようになったと思うのですが。
こんな感じで`CoroutineDispatcher`を定義して`async()`や先程定義した`asyncResponse()`に渡してやれば非同期処理を実行するスレッドを指定させられます。
`bindLaunch()`の`croutineContext`はデフォルトで`UI`を指定しているので何もしなければコルーチンそのものはUIスレッドで起動されます。
そしたら起動したコルーチンの中で、定義した`CoroutineDispatcher`に`CoroutineScope`の`coroutineContext`を持たせて`async`や`AsyncResponse`に渡して呼び出してやれば、非同期処理中に`onDestroy()`が呼ばれた際に適切にコルーチンがキャンセルされます。

`coroutineContext + CoroutineDispatcher`を忘れてそのまま`async()`等に`CoroutineDispatcher`や`kotlinx-coroutines`に定義されている`CommonPool`等を渡すとコルーチンのキャンセルに失敗したりした気がするので、その処理を`CoroutineScope`の拡張プロパティとして定義しておいてやれば忘れにくくていいのかなって思います。
例えば、画像のダウンロード処理をするための`CoroutineDispatcher`とかデータベースに書き込み処理をするための`CoroutineDispatcher`みたいなのを、まとめてどこかのファイルに定義しておいて、そこに一緒に`CoroutineScope`の拡張プロパティとして`coroutineContext`をもたせたものも定義しておく、みたいな感じにすれば実際にasync/awaitを使う際にやらかさなくて済むかなと。

ちなみに`asyncResponse()`の引数`expected`に期待する例外のリストを渡してやれば、それ以外の例外が発生した際には`Failure`に例外を格納して返す、ということをせずにそのまま例外を投げるようにしています。


## 使用した依存関係
build.gradleのdependenciesの部分を抜粋

```gradle
ext.kotlin_version = '1.2.50'
ext.kotlin_croutine_ver = '0.23.2'
ext.arch_ver = '1.1.1'

dependencies {
    //kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$kotlin_croutine_ver"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_croutine_ver"

    //lifecycle
    compileOnly "android.arch.lifecycle:common:$arch_ver"
    compileOnly "android.arch.lifecycle:runtime:$arch_ver"
    testImplementation "android.arch.lifecycle:common:$arch_ver"
    testImplementation "android.arch.lifecycle:runtime:$arch_ver"
    kapt "android.arch.lifecycle:compiler:$arch_ver"
}
```


## おまけ
`Activity`は`onSaveInstanceState()`で`Fragment`の状態を保存するようにしているので、`Activity`の`onSaveInstanceState()`と`onRestoreInstanceState()`の間で`Fragment`をコミットしたりすると例外が投げられてアプリが落ちます。
これでよくあるのが非同期処理した後に`Fragment`を生成して`Activity`に追加したら、非同期処理が終わった時には`onSaveInstanceState()`の後だったみたいな事😌
他にも`onSaveInstanceState()`と`onRestoreInstanceState()`で呼び出されたくない処理ってあると思います。

なので`onResume()`から`onPause()`の間でだけ処理を走らせるためのユーティリティも作ってみました。

```OnActiveRunner.kt
import android.arch.lifecycle.*
import java.lang.ref.WeakReference

/**
 * A interface that run arbitrary functions when [LifecycleOwner] is Active
 */
interface IOnActiveRunner {
    /**
     * Set at the beginning of the life cycle
     * @param owner owner
     * @throws IllegalStateException When already set
     */
    fun setOwner(owner: LifecycleOwner)

    /**
     * @param handle If the [handle] is called after [Lifecycle.Event.ON_PAUSE], the [handle] is called when [Lifecycle.Event.ON_RESUME] is called.
     * @throws IllegalStateException When owner is not set
     */
    fun runOnActive(handle: () -> Unit)
}


class OnActiveRunner : IOnActiveRunner {

    private var mOwnerRef: WeakReference<LifecycleOwner>? = null
    private val mOwner by lazy { requireNotNull(mOwnerRef?.get()) }
    private val mObserver by lazy { createOnActiveLifeCycleObserver(mOwner) }
    private var mIsOwnerInitialized = false

    override fun setOwner(owner: LifecycleOwner) {
        if (mIsOwnerInitialized) throw IllegalStateException("owner already set")
        mOwnerRef = WeakReference(owner)
        owner.lifecycle.addObserver(mObserver)
        mIsOwnerInitialized = true
    }

    override fun runOnActive(handle: () -> Unit) {
        if (!mIsOwnerInitialized) throw IllegalStateException("owner is not set")
        mObserver.run(handle)
    }

    private fun createOnActiveLifeCycleObserver(owner: LifecycleOwner) = object : LifecycleObserver {
        private val mOwnerRef = WeakReference(owner)
        private var isSafe = false
        private val tasks = ArrayList<() -> Unit>()

        fun run(task: () -> Unit) {
            if (isSafe) {
                task()
            } else {
                tasks.add(task)
            }
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        fun onPause() {
            isSafe = false
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        fun onResume() {
            tasks.forEach { it() }
            tasks.clear()
            isSafe = true
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroy() {
            isSafe = false
            val owner1 = mOwnerRef.get() ?: return
            owner1.lifecycle.removeObserver(this)
        }
    }
}
```

使う際はこんな感じ

```SampleActivity.kt
class SampleActivity : AppCompatActivity(), IOnActiveRunner by OnActiveRunner(){

    //onCreateやらなんやら
    ....

    //何かしらの非同期処理後にFragmentを初期化したりするメソッド
    private fun initializeFragment() = bindLaunch {
        val response = downloadHoge().await()
        val result = response.success()?.result ?: return@bindLaunch
        setFragment(result)
    }

    private fun setFragment(hoge:Hoge) = runOnActive {
        //FragmentManagerにFragmentをcommitするとか
    }

}

```

Android Architecture Componentsの`Lifecycle`を使えばいろんなめんどくさい処理を隠せる気がしてきました。