Slide 1

Slide 1 text

ANDROID ASSÍNCRONO De RxJava a Coroutines e WorkManager

Slide 2

Slide 2 text

/_rafaeltoledo /rafaeltoledo

Slide 3

Slide 3 text

O PROBLEMA DO ASSINCRONISMO 3

Slide 4

Slide 4 text

"Eu adoro código assíncrono, nunca dá problema!" 4

Slide 5

Slide 5 text

"Eu adoro código assíncrono, nunca dá problema!" Ninguém, 2018 5

Slide 6

Slide 6 text

Por que é um problema? 6 ● Diminui a legibilidade do código ● Mais difícil de debugar ● Pode trazer problemas de concorrência ● Pode trazer problemas de desempenho

Slide 7

Slide 7 text

lifecycle 7

Slide 8

Slide 8 text

lifecycle 8

Slide 9

Slide 9 text

Abordagens "clássicas" e seus problemas 9 ● AsyncTask - dificuldade em lidar com lifecycle, facilidade em causar leaks de memória ● IntentService - API verbosa e a necessidade de se criar um serviço para cada tarefa (ou uma tarefa com muita lógica) ● Threads - pouco controle (ou controle demais?)

Slide 10

Slide 10 text

RXJAVA 10

Slide 11

Slide 11 text

RxJava - o que é? 11 ● Biblioteca parte das Reactive Extensions ● Mais que uma biblioteca de assincronismo ● Introduz paradigmas de programação reativa funcional ● Tem uma curva de aprendizado íngreme

Slide 12

Slide 12 text

Ah, mas você tá usando RxJava, me explica aí como funciona 12

Slide 13

Slide 13 text

13

Slide 14

Slide 14 text

RxJava - quando eu uso? 14 ● Múltiplas fontes de dados que precisam ser trabalhadas juntas ● Fluxo contínuo de dados ● UI dinâmica (reativa)

Slide 15

Slide 15 text

KOTLIN COROUTINES 15

Slide 16

Slide 16 text

Kotlin Coroutines - o que é? 16 ● Desde o Kotlin 1.1 ● Permite escrever código assíncrono de forma sequencial ● Evita complexidade ● Evita o famoso "callback hell"

Slide 17

Slide 17 text

Kotlin Coroutines - quando eu uso? 17 ● Processamento assíncrono fora da main thread ○ Requests ○ I/O ○ Processamento ● kotlinx-coroutines-android permite fazer a mudança para a main thread!

Slide 18

Slide 18 text

Kotlin Coroutines 18 // Expectativa fun performRequest() { view.showLoading() val result = api.fetch() view.hideLoading() textView.setText(result.data) }

Slide 19

Slide 19 text

19

Slide 20

Slide 20 text

Kotlin Coroutines 20 fun performRequest() = launch(UI) { view.showLoading() val result = withContext(CommonPool) { api.fetch() } view.hideLoading() textView.setText(result.data) }

Slide 21

Slide 21 text

Kotlin Coroutines 21 fun performRequest() = launch(UI) { view.showLoading() val result = withContext(CommonPool) { api.fetch() } view.hideLoading() textView.setText(result.data) }

Slide 22

Slide 22 text

Kotlin Coroutines 22 fun performRequest() = launch(UI) { view.showLoading() val result = withContext(CommonPool) { api.fetch() } view.hideLoading() textView.setText(result.data) }

Slide 23

Slide 23 text

Kotlin Coroutines 23 fun performRequest() = launch(UI) { view.showLoading() val result = withContext(CommonPool) { api.fetch() } view.hideLoading() textView.setText(result.data) }

Slide 24

Slide 24 text

Kotlin Coroutines - Contextos 24 ● UI: executa na main thread ● CommonPool: executa em uma thread em background ● Unconfined: executa na mesma thread

Slide 25

Slide 25 text

Kotlin Coroutines 25 fun performRequest() = launch(UI) { view.showLoading() // Joga exception...? val result = withContext(CommonPool) { api.fetch() } view.hideLoading() textView.setText(result.data) }

Slide 26

Slide 26 text

26

Slide 27

Slide 27 text

Kotlin Coroutines 27 fun performRequest() = launch(UI) { view.showLoading() try { val result = withContext(CommonPool) { api.fetch() } textView.setText(result.data) } catch (exception: IOException) { textView.setText(exception.message) } view.hideLoading() }

Slide 28

Slide 28 text

Kotlin Coroutines - Deferred 28 interface API { fun fetch(): Deferred { // ... } }

Slide 29

Slide 29 text

Kotlin Coroutines 29 fun performRequest() = async(UI) { view.showLoading() try { val task = async(CommonPool) { api.fetch() } val result = task.await() textView.setText(result.data) } catch (exception: IOException) { textView.setText(exception.message) } view.hideLoading() }

Slide 30

Slide 30 text

Kotlin Coroutines 30 fun performRequest() = async(UI) { view.showLoading() try { val task = async(CommonPool) { api.fetch() } val result = task.await() textView.setText(result.data) } catch (exception: IOException) { textView.setText(exception.message) } view.hideLoading() }

Slide 31

Slide 31 text

Mas eu ainda uso Java no meu projeto... 31

Slide 32

Slide 32 text

32

Slide 33

Slide 33 text

WORKMANAGER 33

Slide 34

Slide 34 text

WorkManager - quando usar? Tarefas "adiáveis" - precisam ser executadas, mesmo se o app for fechado, mas não precisam ser executadas em um momento exato É uma solução consistente semelhante ao JobScheduler Ex: upload de logs, sincronização com servidor, tarefas de processamento pesado, que podem demorar bastante 34

Slide 35

Slide 35 text

WorkManager Utiliza em sua implementação JobScheduler, Firebase JobDispatcher ou AlarmManager // Atualmente na versão 1.0.0-alpha04 android.arch.work:work-runtime :work-runtime-ktx :work-testing :work-firebase 35

Slide 36

Slide 36 text

WorkManager - API 36 class FirstWorker : Worker() { override fun doWork(): Result { TODO() } }

Slide 37

Slide 37 text

WorkManager - API 37 class FirstWorker : Worker() { override fun doWork(): Result { return Result.SUCCESS } }

Slide 38

Slide 38 text

WorkManager - API 38 class FirstWorker : Worker() { override fun doWork(): Result { return Result.FAILURE } }

Slide 39

Slide 39 text

WorkManager - API 39 class FirstWorker : Worker() { override fun doWork(): Result { return Result.RETRY } }

Slide 40

Slide 40 text

WorkManager - API 40 class FirstWorker : Worker() { override fun doWork(): Result { inputData applicationContext } }

Slide 41

Slide 41 text

WorkManager - Agendamento Simples 41 val firstWork = OneTimeWorkRequest.Builder(FirstWorker::class.java) .build() WorkManager.getInstance()?.enqueue(firstWork)

Slide 42

Slide 42 text

WorkManager - Agendamento Simples 42 val firstWork = OneTimeWorkRequest.Builder(FirstWorker::class.java) .build() WorkManager.getInstance()?.enqueue(firstWork)

Slide 43

Slide 43 text

WorkManager - Agendamento Simples 43 val firstWork = OneTimeWorkRequestBuilder() .build() WorkManager.getInstance()?.enqueue(firstWork)

Slide 44

Slide 44 text

WorkManager - Agendamento Simples 44 val firstWork = OneTimeWorkRequestBuilder() .setInputData(Data.Builder() .putString("key", "value") .build()) .build() WorkManager.getInstance()?.enqueue(firstWork)

Slide 45

Slide 45 text

WorkManager - Agendamento Simples 45 val firstWork = OneTimeWorkRequestBuilder() .setInputData(mapOf("value" to "key").toWorkData()) .build() WorkManager.getInstance()?.enqueue(firstWork) // Dentro do método doWork() inputData.getString("key", "Default Value")

Slide 46

Slide 46 text

WorkManager - Constraints 46 val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresCharging(true) .setRequiresBatteryNotLow(true) .build() val firstWork = OneTimeWorkRequestBuilder() .setConstraints(constraints) .setInputData(mapOf("value" to "key").toWorkData()) .build()

Slide 47

Slide 47 text

WorkManager - Constraints 47 val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresCharging(true) .setRequiresBatteryNotLow(true) .build() val firstWork = OneTimeWorkRequestBuilder() .setConstraints(constraints) .setInputData(mapOf("value" to "key").toWorkData()) .build()

Slide 48

Slide 48 text

WorkManager - Agendamento Periódico 48 // work-runtime-ktx val periodicWork = PeriodicWorkRequestBuilder(1, TimeUnit.DAYS).build() WorkManager.getInstance()?.enqueue(periodicWork)

Slide 49

Slide 49 text

WorkManager - Testes 49 @Before fun setUp() { WorkManagerTestInitHelper.initializeTestWorkManager( InstrumentationRegistry.getTargetContext()) // ... } @Test fun checkIfJobIsDone() { val worker = ... WorkManager.getInstance()?.enqueue(worker) WorkManagerTestInitHelper.getTestDriver().setAllConstraintsMet(worker.id) // Assertions }

Slide 50

Slide 50 text

Worker - outras possibilidades ● Encadear workers ● Paralelizar workers ● Executar workers com delay 50

Slide 51

Slide 51 text

Worker - pontos importantes ● é executado após o sistema obter um wakelock - não é necessário requisitar ● é executado numa thread em background ● não deve iniciar nenhuma nova thread - deve retornar um status ● não pode rodar pra sempre - ~10 minutos 51

Slide 52

Slide 52 text

Links KOTLIN COROUTINES https://github.com/Kotlin/kotlinx.coroutines A GUIDE TO KOTLIN COROUTINES BY EXAMPLE https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md WORKMANAGER https://developer.android.com/topic/libraries/architecture/workmanager 52

Slide 53

Slide 53 text

Links - vídeos INTRODUCTION TO COROUTINES https://www.youtube.com/watch?v=_hfBv0a09Jc DEEP DIVE INTO COROUTINES ON JVM https://www.youtube.com/watch?v=YrrUCSi72E8 EASY BACKGROUND PROCESSING WITH WORKMANAGER https://www.youtube.com/watch?v=IrKoBFLwTN0 53

Slide 54

Slide 54 text

OBRIGADO Para perguntas ou sugestões: Rafael Toledo [email protected] speakerdeck.com/rafaeltoledo 54