====== coroutines ======
[[https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html|Your first coroutine with Kotlin]]
[[https://github.com/pljp/kotlinx.coroutines/blob/japanese_translation/docs/basics.md|pljp/kotlinx.coroutines]]
===== coroutineの使い方 =====
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
・・・
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2" // step1
}
package com.nekotype.ips.api
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.* // step2
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// click
button.setOnClickListener {
println("start: ${Thread.currentThread().name}")
GlobalScope.launch{
println("coroutine: ${Thread.currentThread().name}")
}
println("end: ${Thread.currentThread().name}")
}
}
}
↓
start: main
end: main
coroutine: DefaultDispatcher-worker-1
===== Threadとは比べ物にならないほど軽い! =====
// thread version
import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicLong
import kotlin.concurrent.thread
fun main() {
val c = AtomicLong()
for (i in 1..1_000_000L)
// thread だと数秒まっても終了しないので諦めた
thread(start = true) {
c.addAndGet(i)
}
println(c.get())
}
↓
// coroutines version
import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicLong
import kotlin.concurrent.thread
fun main() {
runBlocking {
val sum = deferred.sumBy { it.await() }
println("Sum: $sum")
}
}
val deferred = (1..1_000_000).map { n ->
GlobalScope.async {
n
}
}
// Sum: 1784293664
===== launchと違いasyncは値を返す =====
import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicLong
import kotlin.concurrent.thread
fun main() {
runBlocking {
val sum = deferred.sumBy { it.await() }
println("Sum: $sum")
}
}
val deferred = (1..1_000_000).map { n ->
GlobalScope.async {
delay(1000)
n
}
}
↓
Sum: 1784293664
===== mainスレッド(UIスレッド)へのアクセスが簡単 =====
launchやasyncで新しいcoroutineを作成するが、Dispatchers.Mainで簡単にmainスレッドを呼び出せる。
今までのdoInBackgroundでpublishProgressを使ってonProgressUpdateを呼び出すようなことをしなくてよくなる。
package com.nekotype.ips.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import java.text.SimpleDateFormat
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val format = SimpleDateFormat("kk:mm:ss")
Log.d("async", "${text1.text} ${format.format(Date())} : ouerLaunch1 : ${Thread.currentThread().name}\n\n")
GlobalScope.launch {
Log.d("async", "${text1.text} ${format.format(Date())} : inLaunch1 : ${Thread.currentThread().name}\n\n")
async{
delay(3000)
Log.d("async", "${text1.text} ${format.format(Date())} : async1 : ${Thread.currentThread().name}\n\n")
}
Log.d("async", "${text1.text} ${format.format(Date())} : inLaunch2 : ${Thread.currentThread().name}\n\n")
async(Dispatchers.Main) {
delay(1000)
Log.d("async", "${text1.text} ${format.format(Date())} : async2 : ${Thread.currentThread().name}\n\n")
}
async {
delay(500)
Log.d("async","${text1.text} ${format.format(Date())} : async3 : ${Thread.currentThread().name}\n\n")
async(Dispatchers.Main) {
Log.d("async", "${text1.text} ${format.format(Date())} : inAsync3 : ${Thread.currentThread().name}\n\n")
}
}
}
Log.d("async","${text1.text} ${format.format(Date())} : ouerLaunch2 : ${Thread.currentThread().name}\n\n")
}
}
↓
05:32:14 : ouerLaunch1 : main
05:32:14 : ouerLaunch2 : main
05:32:14 : inLaunch1 : DefaultDispatcher-worker-1
05:32:14 : inLaunch2 : DefaultDispatcher-worker-1
05:32:14 : async3 : DefaultDispatcher-worker-1
05:32:14 : inAsync3 : main // ⇒ worker-1 の内側からmainを呼び出している
05:32:15 : async2 : main
05:32:17 : async1 : DefaultDispatcher-worker-3
Log.d( "coroutine","start")
GlobalScope.launch(Dispatchers.Main) {
Log.d( "coroutine","coroutin")
Text.text="click" // 画面のテキストを変更する
}
Log.d( "coroutine","end")
↓
2020-01-14 06:24:17.847 7602-7602/com.nekotype.ips.coroutine D/coroutine: start
2020-01-14 06:24:17.847 7602-7602/com.nekotype.ips.coroutine D/coroutine: end
2020-01-14 06:24:17.849 7602-7602/com.nekotype.ips.coroutine D/coroutine: coroutin
===== キャンセル =====
private var myjob:Job? =null
private var myjobs= mutableListOf()
...
myjob=GlobalScope.launch {
progress_countdown.max=1000
...
// 画面を閉じた場合にコルーチンをキャンセル
override fun onPause() {
super.onPause()
myjobs.map {
Log.d("choice","cancel")
it?.cancel()
}
}
===== join =====
コルーチン内で他のコルーチンの終了を待機する。
fun TestCoroutin(){
Log.d( "coroutine","start")
GlobalScope.launch() {
launch {
Log.d( "coroutine","coroutin_inner")
}
Log.d( "coroutine","coroutin_outer")
}
Log.d( "coroutine","end")
}
↓
2020-01-14 06:29:12.311 7952-7952/com.nekotype.ips.coroutine D/coroutine: start
2020-01-14 06:29:12.340 7952-7952/com.nekotype.ips.coroutine D/coroutine: end
2020-01-14 06:29:12.347 7952-7998/com.nekotype.ips.coroutine D/coroutine: coroutin_outer
2020-01-14 06:29:12.348 7952-7999/com.nekotype.ips.coroutine D/coroutine: coroutin_inner
↓
fun TestCoroutin(){
Log.d( "coroutine","start")
GlobalScope.launch() {
launch {
Log.d( "coroutine","coroutin_inner")
}.join() // 待つ
Log.d( "coroutine","coroutin_outer")
}
Log.d( "coroutine","end")
}
↓
2020-01-14 06:30:51.472 8058-8058/com.nekotype.ips.coroutine D/coroutine: start
2020-01-14 06:30:51.500 8058-8058/com.nekotype.ips.coroutine D/coroutine: end
2020-01-14 06:30:51.505 8058-8111/com.nekotype.ips.coroutine D/coroutine: coroutin_inner //順番が変わる
2020-01-14 06:30:51.508 8058-8110/com.nekotype.ips.coroutine D/coroutine: coroutin_outer //順番が変わる
===== runBlockingとasync =====
runBlockingとasyncは戻り値をもつことができる。
asyncは非同期に処理する。asyncにはawaitでまつことができる。
===== まとめ =====
UIにアクセスするには
GlobalScope.launch(Dispatchers.Main) {...
Jobを取得してキャンセルするには
myjob=GlobalScope.launch { ...
myjob.cancel()
戻り値を取得するには
runBlocking
async
待ちたいとき(同期)
GlobalScope.launch() {
launch { ... }.join()
待ちたいとき(非同期)
GlobalScope.launch() {
async { ... }.await()