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

Programação assíncrona utilizando Coroutines

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

Programação assíncrona utilizando Coroutines

O desenvolvimento de aplicações Android comumente exigem a criação de chamadas assíncronas, que podem acabar gerando diversos problemas como por exemplo o famigerado callback hell. As Coroutines oferecem uma maneira de escrever códigos assíncronos de forma sequencial em uma co-rotina, possibilitando um código mais compacto, fácil de gerenciar e alterar, além de gerar as chamadas lightweight threads, muito mais performáticas que as threads comuns. Nessa palestra vamos mostrar as principais features do Coroutines que podem resolver esse tipo de problema.

Avatar for Diego Santos

Diego Santos

July 21, 2018

More Decks by Diego Santos

Other Decks in Programming

Transcript

  1. Cenário Imagine que você está usando a API do TDC

    e que para usar os serviços é necessário enviar sua chave e receber os tokens de autenticação.
  2. Single Thread fun listEvents() { val token = getToken() val

    events = getEvents(token) displayEvents(events) }
  3. Callbacks fun listEvents() { getTokenAsync { token -> getEventsAsync(token) {

    events -> displayEvents(events) } } } Callback Hell!!
  4. RX R Exemplo de código usando Rx fun listEvents() {

    requestTokenAsync() .flatMap { token -> requestEventsAsync(token) } .subscribe({ events -> displayEvents(events)}) }
  5. Coroutines suspend fun getTokenAsync() {...} suspend fun getEventsAsync() {...} suspend

    fun listEvents() { val token = getTokenAsync() val events = getEventsAsync(token) displayEvents(events) }
  6. Coroutines Coroutines permitem que você escreva seu código de forma

    sequencial e cria light-weight threads para lidar com o código assíncrono.
  7. Coroutines Coroutines precisam de um contexto especial para rodar, portanto

    elas só rodam dentro de Coroutine Builders ou dentro de outras Coroutines
  8. Coroutines Builders Launch - Job Join val job = launch

    { delay(1000L) println("World!") } println("Hello,") job.join()
  9. Coroutines Builders Launch - Job Join val job = launch

    { delay(1000L) println("World!") } println("Hello,") job.join() Fork Coroutine Main Thread Join
  10. Coroutines Builders Launch - Job Join val job = launch

    { delay(1000L) println("World!") } println("Hello,") job.join() Hello, World!
  11. Coroutines Builders Launch - Job Cancel val job = launch

    { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } delay(1300L) // delay a bit println("main: I'm tired of waiting!") job.cancel() // cancels the job job.join() // waits for job's completion println("main: Now I can quit.")
  12. Coroutines Builders Launch - Job Cancel val job = launch

    { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } delay(1300L) // delay a bit println("main: I'm tired of waiting!") job.cancel() // cancels the job job.join() // waits for job's completion println("main: Now I can quit.") I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... main: I'm tired of waiting! main: Now I can quit.
  13. Coroutines Builders Launch - Job Cancel And Join val job

    = launch { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } delay(1300L) // delay a bit println("main: I'm tired of waiting!") job.cancelAndJoin() println("main: Now I can quit.") I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... main: I'm tired of waiting! main: Now I can quit.
  14. Coroutines Builders Launch - Quando a Coroutine para de ser

    processada ? val job = launch { var i = 0 while (i <= 5000) { println("I'm sleeping ${i++} ...") } } delay(1300L) println("main: I'm tired of waiting!") job.cancelAndJoin() println("main: Now I can quit.") I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 … … I'm sleeping 5000 … main: I'm tired of waiting! main: Now I can quit.
  15. Coroutines Builders Launch - Garantindo que a Coroutine vai parar

    quando solicitado val job = launch { var i = 0 while (isActive) { if(i <= 5000) { println("I'm sleeping ${i++} ...")} } delay(1000) println("main: I'm tired of waiting!") job.cancelAndJoin() println("main: Now I can quit.") I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... main: I'm tired of waiting! main: Now I can quit.
  16. Coroutines Builders Run Blocking val job = runBlocking { delay(1000L)

    println("World!") } println("Hello,") job.join()
  17. Coroutines Builders Run Blocking val job = runBlocking { delay(1000L)

    println("World!") } println("Hello,") job.join() World! Hello,
  18. Coroutines Builders Launch - UI Context suspend fun listEvents() {

    launch(UI) { val token = getTokenAsync() val events = getEventsAsync(token) displayEvents(events) } }
  19. Coroutines Builders Launch - Sequencial launch{ val image1 = loadImage1()

    val image2 = loadImage2() setBackgrounds(image1, image2) }
  20. Coroutines Builders Async - Concorrente val image1 = async {

    loadImage1() } val image2 = async { loadImage2() } setBackgrounds(image1.await(), image2.await())
  21. Codifique sequencialmente sem receio! Use loops, high-order functions, etc for

    (i in 1..100_000) { launch { doSomethingHeavy() } }
  22. Cenário Sistema que recebe uma requisição, lê o arquivo local,

    faz o upload desse arquivo e retorna a url para acessar esse arquivo
  23. Coroutines - non-preemptive multi-tasking Usar os recursos de forma eficiente

    e multi-tarefas colaborativa onde as próprias funções controlam o fluxo, disponibilizam a thread e continuam depois de onde pararam, sem a intervenção de um SO.
  24. Coroutines - SÃO light-weight threads val jobs = List(100_000) {

    launch { delay(1000L) print(".") } } jobs.forEach { it.join() }
  25. Direct Style fun listEvents() { val token = getToken() val

    events = getEvents(token) displayEvents(events) } Continuation
  26. Coroutines Direct Style suspend fun listEvents() { val token =

    getToken() val events = getEvents(token) displayEvents(events) } Continuation
  27. Convertendo para CPS suspend fun listEvents() {...} Java/JVM callback Object

    listEvents(Continuation<Void> cont) {...} interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  28. Convertendo para CPS suspend fun listEvents() { val token =

    getToken() val events = getEvents(token) displayEvents(events) } Continuation Inicial
  29. Convertendo para CPS suspend fun listEvents() { val token =

    getToken() val events = getEvents(token) displayEvents(events) } Continuation
  30. Convertendo para CPS suspend fun listEvents() { val token =

    getToken() val events = getEvents(token) displayEvents(events) } Continuation
  31. Convertendo para CPS suspend fun listEvents() { // Label 0

    val token = getToken() // Label 1 val events = getEvents(token) // Label 2 displayEvents(events) }
  32. Convertendo para CPS suspend fun listEvents(Continuation<Void> cont) { val sm

    = object: CoroutineImpl {...} switch(sm.label) { case 0: getToken(sm) case 1: getEvents(sm.token, sm) case 2: displayEvents(sm.events) } }
  33. Convertendo para CPS suspend fun listEvents(Continuation<Void> cont) { val sm

    = object: CoroutineImpl {...} switch(sm.label) { case 0: sm.label = 1 getToken(sm) case 1: ... } }
  34. Convertendo para CPS suspend fun listEvents(Continuation<Void> cont) { val sm

    = object: CoroutineImpl { fun resume(...) { listEvents(this) } } switch(sm.label) { case 0: ... } }
  35. Convertendo para CPS suspend fun listEvents(Continuation<Void> cont) { val sm

    = object: CoroutineImpl {...} switch(sm.label) { case 0: ... case 1: sm.label = 2 getEvents(sm.token, sm) } }
  36. Chamada convencional Retrofit requestToken(). enqueue(object : Callback<T> { override fun

    onResponse(call: Call<T>?, response: Response<T>) { // Handle response } override fun onFailure(call: Call<T>, t: Throwable) { // Handle error } })
  37. Chamada convertida para Coroutine suspend fun <T : Any?> Call<T>.awaitResponse():

    Response<T> { return suspendCancellableCoroutine { continuation -> enqueue(object : Callback<T> { override fun onResponse(call: Call<T>?, response: Response<T>) { if (response.isSuccessful) continuation.resume(response) else continuation.resumeWithException(Throwable()) } override fun onFailure(call: Call<T>, t: Throwable) { if (continuation.isCancelled) return continuation.resumeWithException(t) } }) } }
  38. Chamada convertida para Coroutine suspend fun <T : Any?> Call<T>.awaitResponse():

    Response<T> { ... } val token = requestToken().awaitResponse()
  39. Testando Coroutines • Organizar as camadas da sua aplicação •

    Mantenha o Coroutine Builder na view • Use runBloking no teste • You are free to go!
  40. Libs interessantes usando Coroutines • Kotlin Coroutines for Retrofit https://github.com/gildor/kotlin-coroutines-retrofit

    • Kotlin Coroutine (Experimental) Adapter https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter • Peko https://github.com/deva666/Peko • Anko Coroutines https://github.com/Kotlin/anko/wiki/Anko-Coroutines • Prefekt https://github.com/StylingAndroid/Prefekt
  41. Referências KotlinConf 2017 - Introduction to Coroutines by Roman Elizarov

    https://www.youtube.com/watch?v=_hfBv0a09Jc&t=34s KotlinConf 2017 Deep Dive into Coroutines on JVM by Roman Elizarov https://www.youtube.com/watch?v=YrrUCSi72E8 Github official: https://github.com/Kotlin/kotlinx.coroutines Android + kotlin coroutines: Async de um jeito "diferente", por Walmyr Carvalho (Android Meetup #55) https://www.youtube.com/watch?v=Yr2p4gLUwBA