Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin Coroutines - How to use it right

Kotlin Coroutines - How to use it right

Shared at AndroidCafe.in Meetup #17 - International Women Day Celebration

Yoyo Coder

May 21, 2021
Tweet

More Decks by Yoyo Coder

Other Decks in Programming

Transcript

  1. Hello! I am Yoyo/Thuy I am here because I am

    a woman and love sharing knowledge. 2
  2. 4

  3. 5

  4. 6 Job Deferred<T> join(): Unit await(): T Start a new

    coroutine launch async Return type Suspend till finish
  5. 7 fun main() = runBlocking { val job1 = launch

    { println("Start Job 1") val job2 = launch { println("Start Job 2") delay(100) launch { println("Start Job 3") delay(200) } println("Job 2 - Done!!") } job2.join() println("Job 1 - Done!!") } println("I am not blocking :)") job1.join() println("Done!!") } I am not blocking :) Start Job 1 Start Job 2 Job 2 - Done!! Start Job 3 Job 1 - Done!! Done!!
  6. 8 ThreadPool Thread 1 Thread 2 Dispatcher Coroutine 1 Coroutine

    3 Coroutine 2 waits for Coroutine 2 Coroutine 1 starts Coroutine 2 starts Coroutine 3
  7. Problem 2 I have closed Activity/Fragment that launched a coroutine

    and now its result is no longer needed and its operation should be cancelled.
  8. 10 ViewModelScope LifecycleScope class MyViewModel: ViewModel() { init { viewModelScope.launch

    { // Coroutine that will be canceled when the ViewModel is cleared. } } } class MyFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { // Coroutine that will be canceled when when the Lifecycle is destroyed. } } }
  9. Problem 3 Waiting for a result from a single-shot callback

    API and to return the result to the caller
  10. 12 suspend fun fetchDataFromAPI(): SomeAPIResponse? = suspendCoroutine { continuation ->

    val callback = object : Callback<SomeAPIResponse> { override fun onFailure(call: Call<SomeAPIResponse>,t: Throwable) { continuation.resume(null) } override fun onResponse( call: Call<SomeAPIResponse>, response: Response<SomeAPIResponse> ) { continuation.resume(response.body()) } } retrofit.create(ApiService:: class.java).fetchAPIData().enqueue(callback) }
  11. 14 Flows fun simple(): Flow<Int> = flow { for (i

    in 1..3) { delay(100) // pretend we are asynchronously waiting 100 ms emit(i) // emit next value } } fun main() = runBlocking<Unit> { simple().collect { value -> delay(300) // pretend we are processing it for 300 ms println(value) } }
  12. CancellationException 16 fun main() = runBlocking { val job =

    launch(handler) { throw CancellationException() } job.join() println("Done!!") } fun main() = runBlocking { val job = launch { throw Exception() } job.join() println("Done!!") }
  13. 17 fun main() = runBlocking { val job = launch

    { try { println("Performing network request in Coroutine") delay(1000) } catch (e: Exception) { println("Handled exception in Coroutine") } println("Coroutine still running ... ") } delay(500) job.cancel() }
  14. 18 fun main() = runBlocking { val job = launch

    { try { println("Performing network request in Coroutine") delay(1000) } catch (e: HttpException) { println("Handled exception in Coroutine") } println("Coroutine still running ... ") } delay(500) job.cancel() }
  15. 19 fun main() = runBlocking { val job = launch

    { try { println("Performing network request in Coroutine") delay(1000) } catch (e: Exception) { if (e is CancellationException) { throw e } println("Handled exception in Coroutine") } println("Coroutine still running ... ") } delay(500) job.cancel() }
  16. 21 fun main() { val topLevelScope = CoroutineScope(Job()) topLevelScope.launch {

    try { launch { throw RuntimeException("RuntimeException in nested coroutine") } } catch (exception: RuntimeException) { println("Handle $exception") } } Thread.sleep(100) }
  17. 22 fun main() { val exceptionHandler = CoroutineExceptionHandler { _,

    exception -> println("CoroutineExceptionHandler got $exception") } val topLevelScope = CoroutineScope(Job()) topLevelScope.launch(exceptionHandler) { launch { throw RuntimeException("RuntimeException in nested coroutine") } } Thread.sleep(100) }
  18. 25 Send logs to server Clear logs Add logs log

    1 log 2 log 1 log 2 log 3 log 1 log 2 log 3 Add logs Add logs log 1 log 2 log 3 log 4 log 4 is never sent to server
  19. 26 class LogSynchroniser( ... ) : CoroutineScope { private val

    mutex = Mutex() ... suspend fun addLogs(logs: List<Log>) { mutex.withLock { itemsQueue.addAll(logs) } } fun startSync() { launch { for (event in timer) { mutex.withLock { // Sends logs to server itemsQueue.clear() } } } } }