この文書の現在のバージョンと選択したバージョンの差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
kotlin:coroutines [2019/10/03 06:44] ips |
kotlin:coroutines [2020/01/14 07:03] (現在) ips [まとめ] |
||
---|---|---|---|
ライン 3: | ライン 3: | ||
[[https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html|Your first coroutine with Kotlin]] | [[https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html|Your first coroutine with Kotlin]] | ||
- | Threadとは比べ物にならないほど軽い! | + | [[https://github.com/pljp/kotlinx.coroutines/blob/japanese_translation/docs/basics.md|pljp/kotlinx.coroutines]] |
+ | |||
+ | ===== coroutineの使い方 ===== | ||
+ | |||
+ | <code xml build.gradle (Modlue.app)> | ||
+ | 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 | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code kotlin> | ||
+ | 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 | ||
+ | </code> | ||
+ | |||
+ | |||
+ | |||
+ | ===== Threadとは比べ物にならないほど軽い! ===== | ||
<code kotlin> | <code kotlin> | ||
ライン 47: | ライン 98: | ||
</code> | </code> | ||
+ | ===== launchと違いasyncは値を返す ===== | ||
+ | <code kotlin> | ||
+ | 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 | ||
+ | </code> | ||
+ | |||
+ | ===== mainスレッド(UIスレッド)へのアクセスが簡単 ===== | ||
+ | |||
+ | launchやasyncで新しいcoroutineを作成するが、Dispatchers.Mainで簡単にmainスレッドを呼び出せる。 | ||
+ | 今までのdoInBackgroundでpublishProgressを使ってonProgressUpdateを呼び出すようなことをしなくてよくなる。 | ||
+ | |||
+ | <code kotlin> | ||
+ | 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 | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code kotlin> | ||
+ | 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 | ||
+ | </code> | ||
+ | ===== キャンセル ===== | ||
+ | |||
+ | <code> | ||
+ | private var myjob:Job? =null | ||
+ | private var myjobs= mutableListOf<Job?>() | ||
+ | |||
+ | ... | ||
+ | myjob=GlobalScope.launch { | ||
+ | progress_countdown.max=1000 | ||
+ | ... | ||
+ | | ||
+ | // 画面を閉じた場合にコルーチンをキャンセル | ||
+ | override fun onPause() { | ||
+ | super.onPause() | ||
+ | myjobs.map { | ||
+ | Log.d("choice","cancel") | ||
+ | it?.cancel() | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ===== join ===== | ||
+ | コルーチン内で他のコルーチンの終了を待機する。 | ||
+ | |||
+ | <code kotlin> | ||
+ | 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 | ||
+ | </code> | ||
+ | ↓ | ||
+ | <code kotlin> | ||
+ | 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 //順番が変わる | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ===== 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() |