AsyncTaskと同等の機能をKotlinのCoroutine（コルーチン）で実装

# 検証環境
この記事の内容は、以下の環境で検証しました。

* Java:open jdk 1.8.0_152
* Android Studio 3.0
* CompileSdkVersion:26
* MinSdkVersion:21
* TargetSdkVersion:26
* BuildToolsVersion:26.0.2
* gradle:3.0.0

# AsyncTaskとは
AsyncTaskは、Androidで非同期処理の実装に便利なユーティリティクラスの1つです。
詳細は、下記のサイトを参照してください。
（https://developer.android.com/reference/android/os/AsyncTask.html）

# Coroutineとは
Coroutineとは、Kotlin 1.1から実験的に導入された非同期処理を簡単に実装できる機能です。
async/await/launchなどの関数利用して、非同期処理を実現します。
（本記事ではasync関数で非同期処理を実現します。）
関数の詳細については、下記のサイトを参照してください。
（https://github.com/Kotlin/kotlinx.coroutines）
AsyncTaskと同等の機能をCoroutineで実装した時の差を確認するため、本記事でCoroutine詳細については省略します。
また、キャンセル処理は実装しておりません。

#サンプルアプリ
## 完成イメージ
この記事で作成するサンプルアプリの完成イメージは、下図のとおりです。
![01_アプリイメージ.png](https://qiita-image-store.s3.amazonaws.com/0/99960/e8ed1481-4a9b-0e84-783c-7ff0422663c5.png)


## サンプルアプリの詳細
スタートボタンを押下すると、TextViewの表示が「始めます」に変わります。その後、TextViewに1〜10の値が0.8秒間隔でカウントアップします。カウントアップが終わると、TextViewの表示は「終わります」に変わります。

# ソースコードと解説
サンプルアプリは、以下のファイルで構成されています。
（プロジェクト生成後から変更がないマニフェストファイルなどは、省略しています。）
## ファイル一覧
### レイアウト
* activity_main.xml

### Activity
* MainActivity.kt（AsyncTaskで実装）
* MainActivity.kt（Coroutineで実装）

### Gradle
* build.gradle(app)

## Gradle
AndroidでCoroutineを利用するため、build.gradle(app)のdependenciesに下記の1行を追記します。

```groovy
 compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3"
```

## レイアウト
最終的なレイアウトのイメージは、下図のとおりです。

![02_レイアウト.png](https://qiita-image-store.s3.amazonaws.com/0/99960/4afcf294-e44a-83d4-6595-3185a12cf1e3.png)




### activity_main.xml
画面全体のレイアウトファイルの内容は、以下のとおりです。

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="jp.co.casareal.afterasync.MainActivity">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="30sp" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="スタート"
        android:textSize="30sp" />
</LinearLayout>
```

## Activity

### MainActivity.kt（AsyncTaskで実装）
AsyncTaskでサンプルアプリを実装した時のアクティビティのソースコードは、以下のとおりです。

```kotlin:MainActivity
package jp.co.casareal.asyncbefor

import android.os.AsyncTask
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            MyAsyncTask().execute()
        }
    }

    inner class MyAsyncTask : AsyncTask<Void, Int, Void>() {

        override fun onPreExecute() {
            text.setText("始めます")
            Thread.sleep(800)
        }

        override fun doInBackground(vararg param: Void?): Void? {
            for (i in 1..10) {
                publishProgress(i)
                Thread.sleep(800)
            }
            return null
        }

        override fun onProgressUpdate(vararg values: Int?) {
            text.setText(values[0].toString())
        }

        override fun onPostExecute(result: Void?) {
            text.setText("終わります")
        }

    }
}
```
#### 実装の解説
AsyncTaskを継承したクラスを、インナークラスとして定義しています。
onPreExecuteメソッド、onProgressUpdateメソッド、onPostExecuteメソッドは、UIの変更できるmainスレッドで実行されます。その為、サンプルコードでは、TextViewのプロパティを変更しています。


### MainActivity.kt（Coroutineで実装）

```kotlin
package jp.co.casareal.afterasync

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async

class MainActivity : AppCompatActivity() {

    var job: Deferred<Unit>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            // async関数の戻り（Deferred型）を受け取る
            job = async {
                // myTaskメソッドの呼び出し
                myTask()
            }
        }
    }

    private suspend fun myTask() {
        // onPreExecuteと同等の処理
        async(UI) {
            text.text = "始めます"
        }

        // doInBackgroundメソッドとonProgressUpdateメソッドと
        // 同等の処理
        Thread.sleep(800)
        for (i in 1..10) {
            async(UI) {
                text.text = i.toString()
            }
            Thread.sleep(800)
        }

        // onPostExecuteメソッドと同等の処理
        async(UI) {
            text.text = "終わります"
        }

    }
}
```

#### 実装の解説
非同期処理を実装するために、async関数を利用しています。async関数の定義は下記のサイトを参照してください。
（https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html）

サンプルコードでは、下記の2種類のasync関数の記述方法が出てきます。

##### async関数

```kotlin
import kotlinx.coroutines.experimental.async

async{・・・処理・・・}
```
上記の方法で記述すると、「*mainスレッドではないスレッド*」で処理が実行されます。その為、UIの変更は出来ません。
もし、UIの変更などを行う処理が実行されると、「CalledFromWrongThreadException」が発生します。例外が発生した場合、その子コルーチンの処理は終了しますが、親コルーチンの処理は継続します。

```kotlin
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.android.UI

async(UI) {・・・処理・・・}
```
async関数の引数に処理を実行するスレッドを、指定できます。
上記の方法で記述すると、mainスレッドで処理が実行されます。その為、UIの変更などが行えます。

##### suspend修飾子

コルーチンから呼び出すメソッドや関数は、「suspend」修飾子を必ず付与しなければなりません。

```kotlin
private suspend fun myTask() {・・・処理・・・}
```

# まとめ
AsyncTaskをCoroutineで実装するとコード量が減り、可読性も上がっているように感じます。