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

Android + Kotlin Coroutines: Async de um jeito "diferente"

Android + Kotlin Coroutines: Async de um jeito "diferente"

Slides do um talk realizado no Android Meetup SP, que aconteceu em São Paulo, no dia 6 de Março de 2018.

O talk foi sobre o que são e como utilizar Kotlin Coroutines no Android de um jeito simples e prático.

Link: https://www.meetup.com/pt-BR/GDG-SP/events/248170951/

B567f69eed037ed20536fc825305a38d?s=128

Walmyr Carvalho

March 06, 2018
Tweet

Transcript

  1. Walmyr Carvalho #AndroidMeetupSP Android + Kotlin Coroutines Async de um

    jeito "diferente" +
  2. Walmyr Carvalho @walmyrcarvalho

  3. None
  4. Contexto

  5. Aplicações modernas para Android vão precisar fazer uso de alguma

    operação assíncrona em algum momento.
  6. None
  7. E temos algumas várias (muitas) formas de se fazer isso,

    cada uma com um propósito:
  8. AsyncTask Loader Service JobScheduler/JobSchedulerCompat IntentService Thread FutureTask Firebase JobDispatcher

  9. E hoje temos uma biblioteca “vencedora” no que diz respeito

    a operações assíncronas no Android em geral.
  10. None
  11. Resumindo: Temos muitas opções mas todas elas tem algum ponto

    "negativo” em algum aspecto.
  12. Kotlin Coroutines

  13. Coroutines no Kotlin são threads "mais leves”, com uma sintaxe

    de código assíncrono tão direta quanto a de um código síncrono.
  14. Elas são tipos de funções chamadas suspending functions, e tem

    seus métodos marcados com a palavra chave suspend.
  15. suspend fun getUser(userId: String): User { // return an User

    object }
  16. Na prática, a utilização duma coroutine seria:

  17. // roda o código em uma thread pool em background

    fun asyncOverlay() = async(CommonPool) { // inicia duas operações assíncronas val original = asyncLoadImage("original") val overlay = asyncLoadImage("overlay") // e aplica o overlay para os dois resultados applyOverlay(original.await(), overlay.await()) } // executa a coroutine no contexto de UI launch(UI) { val image = asyncOverlay().await() showImage(image) }
  18. O launch é a maneira mais simples de se criar

    uma coroutine numa thread em background, tendo um Job (uma task em background, basicamente) como sua referência para si.
  19. public actual fun launch( context: CoroutineContext = DefaultDispatcher, start: CoroutineStart

    = CoroutineStart.DEFAULT, parent: Job? = null, block: suspend CoroutineScope.() -> Unit ): Job
  20. Conceitualmente, o async é similar ao launch, mas a diferença

    deles é que o async retorna um Deferred, que é basicamente uma future (ou promise).
  21. public actual fun <T> async( context: CoroutineContext = DefaultDispatcher, start:

    CoroutineStart = CoroutineStart.DEFAULT, parent: Job? = null, block: suspend CoroutineScope.() -> T ): Deferred<T>
  22. O CoroutineContext diz respeito a qual thread ela irá rodar:

    background (CommonPool) ou UI.
  23. Tecnicamente, um Deferred é um Job, só que com uma

    future com uma promessa de entregar um valor em breve.
  24. Ou seja: No async você tem a mesma funcionalidade do

    launch, mas com a diferença que ele retorna um valor no .await():
  25. fun getUser(userId: String): User = async { // return User

    } launch(UI) { val user = getUser(id).await() navigateToUserDetail(user) }
  26. Também é possível rodar alguma operação bloqueando completamente a thread,

    utilizando o runBlocking, mas não é algo muito necessário no Android:
  27. runBlocking { delay(2000) }

  28. Resumindo: Se não faço questão de um callback -> launch

    Se eu preciso esperar uma future (Deferred) -> async Se eu preciso bloquear minha thread -> runBlocking
  29. Também é possível cancelar um Job (ou um Deferred<T>), e

    checar o estado dele:
  30. val job = launch { // do your stuff }

    job.cancel() val job = launch { while (isActive){ //do your stuff } }
  31. Configurando no seu projeto

  32. dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.30' } build.gradle

  33. dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.30" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.5" implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5" } app/build.gradle

  34. kotlin { experimental { coroutines "enable" } } app/build.gradle

  35. Exemplos de uso no Android

  36. Kotlin Coroutines for Retrofit github.com/gildor/kotlin-coroutines-retrofit

  37. fun Call<T>.await(): T

  38. fun main(args: Array<String>) = runBlocking { try { val user:

    User = api.getUser("username").await() println("User ${user.name} loaded") } catch (e: HttpException) { println("exception${e.code()}", e) } catch (e: Throwable) { println("Something broken", e) } }
  39. Retrofit 2 Coroutine Adapter (Experimental) - Jake Wharton github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter

  40. val retrofit = Retrofit.Builder() .baseUrl("https://example.com/") .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build()

  41. interface MyService { @GET("/user") fun getUser(): Deferred<User> // ou @GET("/user")

    fun getUser(): Deferred<Response<User>> }
  42. PEKO (PErmissions in KOtlin) github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter

  43. launch (UI) { val result = Peko.requestPermissions(this, Manifest.permission.BLUETOOTH).await() if (result.grantedPermissions.contains(Manifest.permission.BLUETOOTH))

    { // permissão concedida } else { // nope } }
  44. Dúvidas?

  45. Links úteis

  46. Coroutines - Documentação Oficial kotlinlang.org/docs/reference/coroutines.html

  47. Deep Dives into Coroutines on JVM - Roman Elizarov (KotlinConf

    2017) goo.gl/Vt22LK
  48. Community - Kotlin kotlinlang.org/community/

  49. Kotlin - Android Developers developer.android.com/kotlin

  50. Kotlin Meetup SP meetup.com/kotlin-meetup-sp

  51. Android Dev BR slack.androiddevbr.org

  52. Muito obrigado!