Android / Kotlin / Programming

Android async・launch・withContextってなに?

それぞれの特徴を簡単なコードでまとめました。
runBlockingについてはこちら

async

asyncの特徴として下記が挙げられます。
・並行に処理が行われる
・処理の結果が返される
・await()でjob(処理)が完了するまで待つ必要がある
・await()はメインスレッドをブロックする

import kotlinx.coroutines.*

@ExperimentalCoroutinesApi
fun main() {
    println("Start\n")
    
    runBlocking {
        coroutineScope {
            val async1 = async {
                function1("async")
            }
            val async2 = async {
                function2("async")
            }

            async1.await()
            println("async1.getCompleted(): ${async1.getCompleted()}")
            async2.await()
            println("async2.getCompleted(): ${async2.getCompleted()}")
        }
    }
    
    println("\nEnd")
}

suspend fun function1(name: String): String {
    val funcName = "function1"
    delay(1000)
    println("${name}: ${funcName}")
    
    return funcName
}
 
suspend fun function2(name: String): String {
    val funcName = "function2"
    delay(100)
    println("${name}: ${funcName}")
    
    return funcName
}



/**
* 実行結果
* Start
* 
* async: function2
* async: function1
* async1.getCompleted(): function1
* async2.getCompleted(): function2
* 
* End
**/

実行結果を見てわかる通り、asyncは上から順番に実行されるのではなく並行で実行されます。
そのためdelay()の時間が短いfunction2の方が先に実行され、function1がそれに続きます。
これにより例えばfunction1→function2の順で実行される必要がある場合、16・18行目のようにawait()で待つ必要があります。
しかしawait()はメインスレッドを止めてしまうため、await()中は画面のタップなどが一切できなくなります。

ちなみにawait()をコメントアウトすると終わっていない処理に対して結果を求めることになるため下記エラーが出力されます。

Exception in thread “main” java.lang.IllegalStateException: This job has not completed yet
このjob(async01の処理)はまだ完了していません

launch

launchの特徴として下記が挙げられます。
・並行に処理が行われる
・処理の結果が返されない(jobの進行や完了・未完了は返されます)

import kotlinx.coroutines.*

fun main() {
    println("Start\n")
    
    runBlocking {
        coroutineScope {
            val job1 = launch {
                function1("launch")
            }
            val job2 = launch {
                function2("launch")
            }
            job1.join()
            println("job1.isCompleted: ${job1.isCompleted}")
            job2.join()
            println("job2.isCompleted: ${job2.isCompleted}")
        }
    }
    
    println("\nEnd")
}

suspend fun function1(name: String): String {
    val funcName = "function1"
    delay(1000)
    println("${name}: ${funcName}")
    
    return funcName
}
 
suspend fun function2(name: String): String {
    val funcName = "function2"
    delay(100)
    println("${name}: ${funcName}")
    
    return funcName
}



/**
* 実行結果
* Start
* 
* launch: function2
* launch: function1
* job1.isCompleted: true
* job2.isCompleted: true
* 
* End
**/

launchもasyncと同じように並行で処理を行います。
しかしfunction1と2の返り値を受け取る方法はなく、あくまでjob(処理の進行具合)を取得することしかできません。
そのためlaunchは実行結果の返り値が必要ないデータベースの更新などの処理に使われるようです。

withContext

withContextの特徴として下記が挙げられます。
・直列に処理が行われる
・処理の結果が返される
・待つ必要がない

import kotlinx.coroutines.*

fun main() {
    println("Start\n")
    
    runBlocking {
        coroutineScope {
            val result1 =
            	withContext(Dispatchers.IO) { function1("withContext") }
            val result2 =
                withContext(Dispatchers.IO) { function2("withContext") }
            
            println(result1)
            println(result2)
        }
    }
    
    println("\nEnd")
}

suspend fun function1(name: String): String {
    val funcName = "function1"
    delay(1000)
    println("${name}: ${funcName}")
    
    return funcName
}
 
suspend fun function2(name: String): String {
    val funcName = "function2"
    delay(100)
    println("${name}: ${funcName}")
    
    return funcName
}



/**
* 実行結果
* Start
* 
* withContext: function1
* withContext: function2
* result1: function1
* result2: function2
* 
* End
**/

withContextは上から順に処理を行い、function1の処理が終わるまでは次のラインには進みません。
しかしasyncとは違い、メインスレッドをストップしないためクリックなどの操作を止めることはありません。
また処理の実行結果も返り値として受け取れます。


処理の結果がいらない
Yes -> launch
No -> ↓
並行で処理をする必要がある
Yes -> async
No -> withContext

て感じかなぁと思いました
Flowもあるのでいつかそれについてもまとめようと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です