Kotlin Coroutines & Android

Kotlin Coroutines & Android

80a3a3857a55f154d23acb705eff72cc?s=128

star_zero

July 21, 2020
Tweet

Transcript

  1. Kotlin Coroutines & Android Kenji Abe / @STAR_ZERO Android, Kotlin

    GDE Cookpad Inc.
  2. This class was deprecated in API level 30. Use the

    standard java.util.concurrent or Kotlin concurrency utilities instead. AsyncTask
  3. Title safe Actio n safe Kotlin Coroutines are the recommended

    solution for async code.
  4. Coroutines

  5. fun request(callback: (Data) -> Unit) { // Network access }

    fun loadData() { request { data -> show(data) } } Callback
  6. fun loadData() { request1 { data1 -> request2 { data2

    -> request3 { data3 -> request4 { data4 -> request5 { data5 -> // ... } } } } } } Callback
  7. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines
  8. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines
  9. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines
  10. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines request show Background Thread
  11. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines request show Background Thread
  12. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines request show Background Thread 待機
  13. suspend fun request(): String = withContext(Dispatchers.IO) { // Network access

    } suspend fun loadData() { val data = request() show(data) } Coroutines request show Background Thread
  14. CoroutineDispatcher

  15. CoroutineDispatcher Main IO Default UI Network & Disk CPU

  16. suspend fun request(): String = withContext(Dispatchers.IO) { // ... }

    CoroutineDispatcher
  17. suspend fun run() { withContext(Dispatchers.IO) { // ... } withContext(Dispatchers.Default)

    { // ... } withContext(Dispatchers.Main) { // ... } } CoroutineDispatcher
  18. CoroutineScope / CoroutineBuilder

  19. ० CoroutineScope ◦ Coroutineのライフサイクルの管理 ◦ キャンセルや例外処理の対応 ० CoroutineBuilder ◦ 新しくCoroutineを作成して実行する

    ◦ launch / async CoroutineScope / CoroutineBuilder
  20. fun event() { request() } suspend fun request() { //

    ... } CoroutineScope / CoroutineBuilder
  21. fun event() { request() } suspend fun request() { //

    ... } CoroutineScope / CoroutineBuilder Suspend function 'request' should be called only from a coroutine or another suspend function
  22. val scope = CoroutineScope(Dispatchers.Main) fun event() { scope.launch { request()

    } } CoroutineScope / CoroutineBuilder
  23. val scope = CoroutineScope(Dispatchers.Main) fun event() { scope.launch { request()

    } } CoroutineScope / CoroutineBuilder CoroutineScope 作成
  24. val scope = CoroutineScope(Dispatchers.Main) fun event() { scope.launch { request()

    } } CoroutineScope / CoroutineBuilder Coroutine 作成
  25. Cancellation

  26. val job = scope.launch { request() } // ... job.cancel()

    // or scope.cancel() Cancellation
  27. val job = scope.launch { while(true) { // ... println("Hello")

    } } // ... job.cancel() Cancellation
  28. val job = scope.launch { while(true) { // ... println("Hello")

    } } // ... job.cancel() Cancellation Hello Hello Hello Hello Hello Hello . .
  29. val job = scope.launch { while(isActive) { // ... println("Hello")

    } } // ... job.cancel() Cancellation キャンセルされているか確認
  30. val job = scope.launch { while(isActive) { ensureActive() // ...

    println("Hello") } } // ... job.cancel() Cancellation キャンセルされている場合 CancellationException
  31. async / await

  32. suspend fun request(): Data { delay(3000) // ... } val

    time = measureTimeMillis { val data1 = request() val data2 = request() } println("time = $time") async / await
  33. suspend fun request(): Data { delay(3000) // ... } val

    time = measureTimeMillis { val data1 = request() val data2 = request() } println("time = $time") async / await 約 6000 ms
  34. val time = measureTimeMillis { val deferred1 = async {

    request() } val deferred2 = async { request() } val data1 = deferred1.await() val data2 = deferred2.await() } println("time = $time") async / await
  35. val time = measureTimeMillis { val deferred1 = async {

    request() } val deferred2 = async { request() } val data1 = deferred1.await() val data2 = deferred2.await() } println("time = $time") async / await 処理開始して待たずに次へ
  36. val time = measureTimeMillis { val deferred1 = async {

    request() } val deferred2 = async { request() } val data1 = deferred1.await() val data2 = deferred2.await() } println("time = $time") async / await 完了を待って結果を返す
  37. val time = measureTimeMillis { val deferred1 = async {

    request() } val deferred2 = async { request() } val data1 = deferred1.await() val data2 = deferred2.await() } println("time = $time") async / await 約 3000 ms
  38. Exception

  39. val scope = CoroutineScope(Dispatchers.Default) scope.launch { throw IllegalStateException() } Exception

  40. val deferred = async { throw IllegalStateException() } deferred.await() Exception

  41. val handler = CoroutineExceptionHandler { _, throwable -> // ...

    } val scope = CoroutineScope(Dispatchers.Default + handler) scope.launch { throw IllegalStateException() } Exception
  42. val handler = CoroutineExceptionHandler { _, throwable -> // ...

    } val scope = CoroutineScope(Dispatchers.Default + handler) scope.launch { throw IllegalStateException() } Exception
  43. Flow

  44. ० 複数の値 ० Cold Stream (Terminal operatorsで処理を開始) ० 様々なオペレーター Flow

  45. fun createFlow(): Flow<Int> { return flow { (1..10).forEach { emit(it)

    } } } scope.launch { val flow = createFlow() flow.collect { println(it) } } Flow
  46. fun createFlow(): Flow<Int> { return flow { (1..10).forEach { emit(it)

    } } } scope.launch { val flow = createFlow() flow.collect { println(it) } } Flow Flowを作成
  47. fun createFlow(): Flow<Int> { return flow { (1..10).forEach { emit(it)

    } } } scope.launch { val flow = createFlow() flow.collect { println(it) } } Flow Flowはまだ動かない
  48. fun createFlow(): Flow<Int> { return flow { (1..10).forEach { emit(it)

    } } } scope.launch { val flow = createFlow() flow.collect { println(it) } } Flow ここで動きだす
  49. fun createFlow(): Flow<Int> { return flow { (1..10).forEach { emit(it)

    } } } scope.launch { val flow = createFlow() flow.collect { println(it) } } Flow 値を送信 emit された値が通知される
  50. flow.map { it * it }.filter { it % 3

    == 0 }.collect { // ... } Flow
  51. suspendCancellableCoroutine callbackFlow

  52. suspend fun run() = suspendCancellableCoroutine<Response> { continuation -> request(object :

    Callback { override fun success(response: Response) { continuation.resume(response) } override fun failure(e: Exception) { continuation.resumeWithException(e) } }) continuation.invokeOnCancellation { cancel() } } suspendCancellableCoroutine
  53. suspend fun run() = suspendCancellableCoroutine<Response> { continuation -> request(object :

    Callback { override fun success(response: Response) { continuation.resume(response) } override fun failure(e: Exception) { continuation.resumeWithException(e) } }) continuation.invokeOnCancellation { cancel() } } suspendCancellableCoroutine
  54. suspend fun run() = suspendCancellableCoroutine<Response> { continuation -> request(object :

    Callback { override fun success(response: Response) { continuation.resume(response) } override fun failure(e: Exception) { continuation.resumeWithException(e) } }) continuation.invokeOnCancellation { cancel() } } suspendCancellableCoroutine suspend funの戻り値
  55. suspend fun run() = suspendCancellableCoroutine<Response> { continuation -> request(object :

    Callback { override fun success(response: Response) { continuation.resume(response) } override fun failure(e: Exception) { continuation.resumeWithException(e) } }) continuation.invokeOnCancellation { cancel() } } suspendCancellableCoroutine throw Exception
  56. suspend fun run() = suspendCancellableCoroutine<Response> { continuation -> request(object :

    Callback { override fun success(response: Response) { continuation.resume(response) } override fun failure(e: Exception) { continuation.resumeWithException(e) } }) continuation.invokeOnCancellation { cancel() } } suspendCancellableCoroutine Coroutineがキャンセルされた
  57. fun run() = callbackFlow<Data> { val callback = object :

    Callback { override fun onReceive(data: Data) { offer(data) } } registerCallback(callback) awaitClose { unregisterCallback(callback) } } callbackFlow
  58. fun run() = callbackFlow<Data> { val callback = object :

    Callback { override fun onReceive(data: Data) { offer(data) } } registerCallback(callback) awaitClose { unregisterCallback(callback) } } callbackFlow
  59. fun run() = callbackFlow<Data> { val callback = object :

    Callback { override fun onReceive(data: Data) { offer(data) } } registerCallback(callback) awaitClose { unregisterCallback(callback) } } callbackFlow 値を送信
  60. fun run() = callbackFlow<Data> { val callback = object :

    Callback { override fun onReceive(data: Data) { offer(data) } } registerCallback(callback) awaitClose { unregisterCallback(callback) } } callbackFlow キャンセルされるまで待機
  61. Coroutines Android

  62. Room

  63. @Dao interface UserDao { @Query("SELECT * FROM user") suspend fun

    getAll(): List<User> } Room
  64. @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll():

    Flow<List<User>> } Room
  65. WorkManager

  66. class MyWorker( context: Context, params: WorkerParameters ) : CoroutineWorker(context, params)

    { override suspend fun doWork(): Result { val data = readData() upload(data) return Result.success() } } WorkManager
  67. CoroutineScope

  68. viewModelScope.launch { // ... } viewModelScope init Cancelled Active onCleared

  69. lifecycleScope.launch { // ... } lifecycleScope onCreate onPause Cancelled Active

    onStart onResume onDestroy onStop init
  70. lifecycleScope.launchWhenCreated { // ... } lifecycleScope onCreate onPause Cancelled Active

    onStart onResume onDestroy onStop init Pause
  71. lifecycleScope.launchWhenStarted { // ... } lifecycleScope onCreate onPause Cancelled Pause

    onStart onResume onDestroy onStop init Active Pause
  72. lifecycleScope.launchWhenResumed { // ... } lifecycleScope onCreate onPause onStart onResume

    onDestroy onStop init Cancelled Pause Active Pause
  73. LiveData

  74. val data: LiveData<String> = liveData { // ... emit("test") }

    LiveData
  75. val data: LiveData<String> = liveData { // ... emit("test") }

    LiveData suspend
  76. val data: LiveData<String> = liveData { // ... emit("test") }

    LiveData 値を送信
  77. val data = MutableLiveData(0) val flow = data.asFlow() scope.launch {

    flow.collect { // ... } } LiveData
  78. val data = MutableLiveData(0) val flow = data.asFlow() scope.launch {

    flow.collect { // ... } } LiveData LiveData → Flow
  79. val flow = flow { // ... emit("test") } val

    liveData = flow.asLiveData() liveData.observe(this) { // ... } LiveData
  80. val flow = flow { // ... emit("test") } val

    liveData = flow.asLiveData() liveData.observe(this) { // ... } LiveData Flow → LiveData
  81. asLiveData vs lifecycleScope.launchWhenStarted

  82. fun FusedLocationProviderClient.locationFlow() = callbackFlow<Location> { val callback = object :

    LocationCallback() { override fun onLocationResult(result: LocationResult?) { result ?: return try { offer(result.lastLocation) } catch(e: Exception) {} } } requestLocationUpdates(/* ... */) .addOnFailureListener { e -> close(e) // in case of exception, close the Flow } // clean up when Flow collection ends awaitClose { removeLocationUpdates(callback) } } asLiveData vs lifecycleScope.launchWhenStarted
  83. myFlow.asLiveData().observe { // ... } lifecycleScope.launchWhenStarted { myFlow.collect { //

    ... } } asLiveData vs lifecycleScope.launchWhenStarted
  84. asLiveData vs lifecycleScope.launchWhenStarted Activity onStart Active Active onStop asLiveData lifecycleScope.launchWhenStarted

    Observer: Flow: Active Active Collector: Flow:
  85. asLiveData vs lifecycleScope.launchWhenStarted Activity onStart Active Active onStop asLiveData lifecycleScope.launchWhenStarted

    Cancelled Cancelled Observer: Flow: Observer: Flow: Active Active Paused Active Collector: Flow: Collector: Flow:
  86. And more...

  87. New feature

  88. StateFlow

  89. class ViewModel { private val _counter = MutableStateFlow(0) val counter:

    StateFlow<Int> = _counter fun increment() { _counter.value++ } } lifecycleScope.launchWhenStarted { viewModel.counter.collect { println(it) } } StateFlow
  90. ० FlowのHot Stream ० 読み込み専用のStateFlow / 書込み可能なMutableStateFlow ० 様々なオペレーター StateFlow

  91. MutableLiveData<T>: var value: T fun postValue(value: T) LiveData<T>: val value:

    T LiveData vs StateFlow MutableStateFlow<T>: var value: T StateFlow<T>: val value: T
  92. LiveData vs StateFlow • Data Holder • LifecycleOwner • 同じ値も通知される

    • 初期値は通知されない • Transformations • Flow (Hot Stream) • CoroutineScope • 同じ値は通知されない • 初期値も通知 • オペレーター LiveData StateFlow
  93. Appendix goo.gle/coroutines-posts • Coroutines: First things first • Cancellation in

    coroutines • Exceptions in Coroutines • The suspend modifier — Under the hood goo.gle/coroutines-101 • Coroutines 101 codelabs.developers.google.com • Use Kotlin Coroutines in your Android App • Learn advanced coroutines with Kotlin Flow and LiveData • Building a Kotlin extensions library
  94. Thank you!