KotlinのCoroutines 1.3でシンプルにコールバック待ちをする

# はじめに
こんにちは。今年ACCESSに入社したk.nagauchiです。
GT-Rのアイコンについてよく聞かれますが、昔から好きで一時期乗っていた（[動画](https://www.youtube.com/watch?v=sbt0iiYxi-I)）ので、
今も自分のIdentityとして使ってます。

現在KotlinとSwiftを使うスマホアプリ開発業務（と時間が許せばFlutter）に力を入れています。
[ACCESS Advent Calendar 2018](https://qiita.com/advent-calendar/2018/access) の13日目の記事を担当いたします。

# 今回の内容
KotlinのCoroutines（コルーチン）で、シンプルにコールバック待ちをしたい時の実装テンプレです。
Javaからの移民向けです。
シンプルと言いつつJavaライクな記法で書いているのはそんな理由もあります。

# 実装例
通信処理MyConnectionService.connect()が時間のかかる非同期コールバック持ちメソッドになっているが、
それを使う自分のsyncConnect()はreturnで結果を返す同期メソッドにしたい場合。

## メソッド

```kotlin
suspend fun syncConnect() : Boolean {
    return suspendCoroutine { continuation ->
        val callback = object: MyConnectionCallback {
            override fun onConnected() {
                continuation.resume(true) // ここでsyncConnect()をreturnする
            }
            override fun onError(error: Error) {
                Log.e(LOG_TAG, this.javaClass.simpleName + ":onError:" + error.message);
                continuation.resume(false) // ここでsyncConnect()をreturnする
            }
        }
        MyConnectionService.connect(callback)
        return@suspendCoroutine // resumeが呼ばれるまで待つ
    }
}
```

## 呼び出し部分

```kotlin
class MyActivity : AppCompatActivity(), CoroutineScope {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(LOG_TAG, "syncConnect() start.")
        launch(Dispatchers.Default) {
            syncConnect()
        }.join()
        Log.d(LOG_TAG, "syncConnect() finish.")
    }
}
```

もしくは

```kotlin
suspend fun doSyncConnect() : Boolean {
    Log.d(LOG_TAG, "syncConnect() start.")
    val result = GlobalScope.async(Dispatchers.Default) {
        syncConnect()
    }.await()
    Log.d(LOG_TAG, "syncConnect() finish.")
    return result
}
```

# 実装例2

callbackではなくstartActivityを挟むケースでも、Activityにcallbackを渡しておいて、
Activity側でcallback.xxxxx()をコールすれば同じことができると思います。

## メソッド

```kotlin
private suspend fun grantPermission() : Boolean {
    return suspendCoroutine { continuation ->
        val callback = object: MyPermissionCallback {
            override fun onGranted() {
                continuation.resume(true) // ここでgrantPermission()をreturnする
            }
            override fun onDenied(reason: String) {
                Log.e(LOG_TAG, this.javaClass.simpleName + ":onDenied:" + reason);
                continuation.resume(false) // ここでgrantPermission()をreturnする
            }
        }
        MyPermissionActivity.callback = callback // Activity側でcallbackはcompanion objectにしておく
        activity.startActivity(Intent(activity, MyPermissionActicity::class.java))
        return@suspendCoroutine // resumeが呼ばれるまで待つ
    }
}
```

## 呼び出し部分

```kotlin
class MyActivity : AppCompatActivity(), CoroutineScope {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(LOG_TAG, "grantPermission() start.")
        launch(Dispatchers.Default) {
            grantPermission()
        }.join()
        Log.d(LOG_TAG, "grantPermission() finish.")
    }
}
```

もしくは

```kotlin
suspend fun doGrantPermission() : Boolean {
    Log.d(LOG_TAG, "grantPermission() start.")
    val result = GlobalScope.async(Dispatchers.Default) {
        grantPermission()
    }.await()
    Log.d(LOG_TAG, "grantPermission() finish.")
    return result
}
```

# 説明
suspendCoroutineを付けてreturnすると、メソッドの処理が中断されます。
**中断中は実行スレッドをブロックしません**。ここがCoroutinesの一番の強みです。

resumeを呼ぶと再開し、その引数をsyncConnectやgrantPermissionの結果として呼び元に返します。

ただし、CoroutineScopeを介さず呼ぶ場合、syncConnectやgrantPermissionの呼び元にもsuspendを付けなければならないのが難点なのと、
コルーチン入門者がとりあえずうまくいったという程度の記事なので、問題がある場合は教えてください。

それでは、明日は @aRyoKuroda さんです。お楽しみに！