Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Kotlin Coroutines & Android

Kotlin Coroutines & Android

star_zero

July 21, 2020
Tweet

More Decks by star_zero

Other Decks in Programming

Transcript

  1. This class was deprecated in API level 30. Use the

    standard java.util.concurrent or Kotlin concurrency utilities instead. AsyncTask
  2. fun request(callback: (Data) -> Unit) { // Network access }

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

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

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

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

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

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

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

    } suspend fun loadData() { val data = request() show(data) } Coroutines request show Background Thread 待機
  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 run() { withContext(Dispatchers.IO) { // ... } withContext(Dispatchers.Default)

    { // ... } withContext(Dispatchers.Main) { // ... } } CoroutineDispatcher
  12. fun event() { request() } suspend fun request() { //

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

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

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

    } } CoroutineScope / CoroutineBuilder Coroutine 作成
  16. val job = scope.launch { while(true) { // ... println("Hello")

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

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

    println("Hello") } } // ... job.cancel() Cancellation キャンセルされている場合 CancellationException
  19. suspend fun request(): Data { delay(3000) // ... } val

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

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

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

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

    request() } val deferred2 = async { request() } val data1 = deferred1.await() val data2 = deferred2.await() } println("time = $time") async / await 完了を待って結果を返す
  24. 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
  25. val handler = CoroutineExceptionHandler { _, throwable -> // ...

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

    } val scope = CoroutineScope(Dispatchers.Default + handler) scope.launch { throw IllegalStateException() } Exception
  27. fun createFlow(): Flow<Int> { return flow { (1..10).forEach { emit(it)

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

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

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

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

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

    == 0 }.collect { // ... } Flow
  33. 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
  34. 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
  35. 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の戻り値
  36. 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
  37. 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がキャンセルされた
  38. fun run() = callbackFlow<Data> { val callback = object :

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

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

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

    Callback { override fun onReceive(data: Data) { offer(data) } } registerCallback(callback) awaitClose { unregisterCallback(callback) } } callbackFlow キャンセルされるまで待機
  42. class MyWorker( context: Context, params: WorkerParameters ) : CoroutineWorker(context, params)

    { override suspend fun doWork(): Result { val data = readData() upload(data) return Result.success() } } WorkManager
  43. val data = MutableLiveData(0) val flow = data.asFlow() scope.launch {

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

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

    liveData = flow.asLiveData() liveData.observe(this) { // ... } LiveData Flow → LiveData
  46. 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
  47. 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:
  48. class ViewModel { private val _counter = MutableStateFlow(0) val counter:

    StateFlow<Int> = _counter fun increment() { _counter.value++ } } lifecycleScope.launchWhenStarted { viewModel.counter.collect { println(it) } } StateFlow
  49. 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
  50. LiveData vs StateFlow • Data Holder • LifecycleOwner • 同じ値も通知される

    • 初期値は通知されない • Transformations • Flow (Hot Stream) • CoroutineScope • 同じ値は通知されない • 初期値も通知 • オペレーター LiveData StateFlow
  51. 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