検証環境
この記事の内容は、以下の環境で検証しました。
- 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詳細については省略します。
また、キャンセル処理は実装しておりません。
#サンプルアプリ
完成イメージ
この記事で作成するサンプルアプリの完成イメージは、下図のとおりです。
サンプルアプリの詳細
スタートボタンを押下すると、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行を追記します。
compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3"
レイアウト
最終的なレイアウトのイメージは、下図のとおりです。
activity_main.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でサンプルアプリを実装した時のアクティビティのソースコードは、以下のとおりです。
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で実装)
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関数
import kotlinx.coroutines.experimental.async
async{・・・処理・・・}
上記の方法で記述すると、「mainスレッドではないスレッド」で処理が実行されます。その為、UIの変更は出来ません。
もし、UIの変更などを行う処理が実行されると、「CalledFromWrongThreadException」が発生します。例外が発生した場合、その子コルーチンの処理は終了しますが、親コルーチンの処理は継続します。
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.android.UI
async(UI) {・・・処理・・・}
async関数の引数に処理を実行するスレッドを、指定できます。
上記の方法で記述すると、mainスレッドで処理が実行されます。その為、UIの変更などが行えます。
suspend修飾子
コルーチンから呼び出すメソッドや関数は、「suspend」修飾子を必ず付与しなければなりません。
private suspend fun myTask() {・・・処理・・・}
まとめ
AsyncTaskをCoroutineで実装するとコード量が減り、可読性も上がっているように感じます。