SlideShare uma empresa Scribd logo
1 de 82
Baixar para ler offline
Coroutines & Jetpack
Aplicações assíncronas no Android com
Nelson Glauber
@nglauber

slideshare.com/nglauber

youtube.com/nglauber
A main thread do Android
• Carregar arquivo de layout

• Desenhar as views

• Tratar os eventos de UI

• …
A main thread do Android
A main thread do Android
Solução?
Async no Android
• AsyncTask !

• Thread + Handler "

• Loaders (deprecated) !

• Volley #$

• RxJava 🧐
Coroutines
• Essencialmente, coroutines são light-weight threads. 

• Fácil de usar (sem mais "callbacks hell” e/ou centenas de
operadores).

• Úteis para operações de I/O ou qualquer outra tarefa
computacional mais onerosa.

• Permite a substituição de callbacks por operações
assíncronas.
Dependências
dependencies {
def coroutines_version = '1.2.1'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
...
}
suspend
• Suspending functions são o centro de tudo em Coroutines.
Elas são basicamente funções que podem ser pausadas e
retomadas após algum tempo. 

• Podem executar longas tarefas e aguardar o resultado sem
bloquear a thread atual.

• A sintaxe é idêntica a uma função “normal”, exceto pela
adição da palavra reservada suspend.

• Por si só, uma suspending function não é assíncrona.

• Só pode ser chamada a partir de outra suspending function.
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.Assert.assertEquals
class CalculatorUnitTest {
@Test
fun sum_isCorrect() = runBlocking {
val calc = Calculator()
assertEquals(4, calc.sum(2, 2))
}
}
import kotlinx.coroutines.delay
class Calculator {
suspend fun sum(a: Int, b: Int): Int {
delay(5_000)
return a + b
}
}
Coroutines no Android
• Para entendermos melhor as Coroutines no Android
precisamos falar de 4 conceitos:

• Job

• Context

• Scope

• Dispatcher
class MainActivity : AppCompatActivity() {
private val job = Job()
private val coroutineScope = CoroutineScope(job + Dispatchers.Main)
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
class MainActivity : AppCompatActivity() {
private val job = Job()
private val coroutineScope = CoroutineScope(job + Dispatchers.Main)
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
Job
• Um Job representa uma tarefa ou conjunto de
tarefas em background.

• A função launch retorna um Job.

• Mantém a referência do código que está em
execução.

• Pode possuir “filhos”.

• Pode ser cancelado usando a função cancel.
class MainActivity : AppCompatActivity() {
private val job = Job()
private val coroutineScope = CoroutineScope(job + Dispatchers.Main)
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
Context
• É um conjunto de atributos que configuram
uma coroutine. 

• Representada pela interface
CoroutineContext.

• Pode definir a política de threading,
tratamento de exceções, etc.
class MainActivity : AppCompatActivity() {
private val job = Job()
private val coroutineScope = CoroutineScope(job + Dispatchers.Main)
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
Scope
• Um escopo serve como uma espécie de ciclo de
vida para um conjunto de coroutines.

• Coroutines sempre rodam em um escopo.

• Permite um maior controle das tarefas em
execução
GlobalScope
• O GlobalScope, como o nome sugere, é utilizado em tarefas cujo o escopo é
global da aplicação.

• O GlobalScope é considerado um anti-pattern no Android e deve ser evitado.
private suspend fun loadName(): String {
delay(5000)
return "Glauber"
}
private fun firstCoroutine() {
GlobalScope.launch {
val name = loadName()
println("$name!")
}
println("Hello, ")
}
class MainActivity : AppCompatActivity() {
private val job = Job()
private val coroutineScope = CoroutineScope(job + Dispatchers.Main)
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
Dispatcher
• Um dispatcher define o pool de threads
que a coroutine executará.

• Default - É otimizado para processos
que usam a CPU mais intensamente.

• IO - recomendado para tarefas de rede
ou arquivos. O pool de threads é
compartilhado com o dispatcher
DEFAULT.

• Main - main thread do Android.
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
addTextToTextView("${it.title}n")
}
}
}
&
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = BookHttp.loadBooks()
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1513)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:117)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105)
at java.net.InetAddress.getAllByName(InetAddress.java:1154)
at com.android.okhttp.Dns$1.lookup(Dns.java:39)
FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: br.com.nglauber.coroutinesdemo, PID: 26507
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that
created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
fun callWebService() {
coroutineScope.launch(Dispatchers.IO){
txtOutput.text = ""
val books = BookHttp.loadBooks()
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
class MainActivity : AppCompatActivity() {
private val job = Job()
private val coroutineScope = CoroutineScope(job + Dispatchers.Main)
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
class MainActivity : AppCompatActivity(), CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
fun callWebService() {
launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
books?.forEach {
txtOutput.append("${it.title}n")
}
}
}
...
Lifecycle
Lifecycle Scope
• Escopo atrelado ao ciclo de vida da Activity, Fragment ou
View do Fragment

• Além da função launch, podemos usar o
launchWhenCreated, launchWhenStarted e
launchWhenResumed.
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01"
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
lifecycleScope.launch {
...
}
// or...
lifecycleScope.launchWhenCreated {
...
}
// or...
lifecycleScope.launchWhenStarted {
...
}
}
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
lifecycleScope.launch {
...
}
// or...
lifecycleScope.launchWhenCreated {
...
}
// or...
lifecycleScope.launchWhenStarted {
...
}
}
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
...
}
}
}
ViewModel
ViewModel Scope




Um ViewModel agora possui a propriedade viewModelScope.
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01"
testImplementation "androidx.arch.core:core-testing:2.1.0-beta01"
}
class MainViewModel : ViewModel() {
private val _message = MutableLiveData<String>()
val message: LiveData<String> = _message
fun loadMessage() {
viewModelScope.launch {
val message = withContext(Dispatchers.IO) {
loadMessageFromNetwork()
}
_message.value = message
}
}
private suspend fun loadMessageFromNetwork() : String {
delay(2_000)
return "Hello from ViewModel"
}
}
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by lazy {
ViewModelProviders.of(this).get(MainViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
viewModel.message.observe(this, Observer { s ->
txtOutput.text = s
})
viewModel.loadMessage()
}
...
WorkManager
WorkManager
dependencies {
def work_version = "2.1.0-beta02"
implementation "androidx.work:work-runtime-ktx:$work_version"
androidTestImplementation "androidx.work:work-testing:$work_version"
}
class MyWork(context: Context, params: WorkerParameters) :
CoroutineWorker(context, params) {
override suspend fun doWork(): Result = try {
val output = inputData.run {
val x = getInt("x", 0)
val y = getInt("y", 0)
val result = Calculator().sum(x, y)
workDataOf("result" to result)
}
Result.success(output)
} catch (error: Throwable) {
Result.failure()
}
}
private fun callMyWork() {
val request =
OneTimeWorkRequestBuilder<MyWork>()
.setInputData(workDataOf("x" to 84, "y" to 12))
.build()
WorkManager.getInstance(this).run {
enqueue(request)
getWorkInfoByIdLiveData(request.id)
.observe(this@MainActivity, Observer {
if (it.state == WorkInfo.State.SUCCEEDED) {
val result = it.outputData.getInt("result", 0)
addTextToTextView("Result-> $result")
}
})
}
voltando às coroutines…
Iniciando uma coroutine
• As duas formas de iniciar uma coroutine são:

• A função launch é uma “fire and forget”  que significa
que não retornará o resultado para que a chamou (mas
retornará um Job).

• A função async retorna um objeto Deferred que permite
obter o seu resultado.
launch = Sequencial
private fun sequentialCalls() {
txtOutput.text = ""
launch {
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
addTextToTextView("The answer is ${one + two}")
}
addTextToTextView("Completed in $time ms")
}
}
The answer is 42

Completed in 2030 ms
async = Paralelo
private fun parallelCalls() {
txtOutput.text = ""
launch {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
addTextToTextView("The answer is ${one.await() + two.await()} ")
}
addTextToTextView("Completed in $time ms")
}
}
The answer is 42

Completed in 1038 ms
Launch x Async
Em quase todos os casos, deve-se usar a função
launch para iniciar uma coroutine a partir de uma
função “normal”. 

Uma função comum não pode chamar o await
(porque ela é uma suspending function), então
não faz muito sentido usar o async como ponto
de partida.
Exceptions
• As exceções tem comportamentos diferentes para o
launch e o async. 
Exceptions - launch
private fun handlingException() {
launch {
txtOutput.text = ""
try {
val a = methodThatThrowsException()
addTextToTextView("Ok ${a}")
} catch (e: Exception) {
addTextToTextView("handlingException caught: ${e.message}")
}
}
}
Exceptions - async
private fun handlingAsyncException() {
launch {
txtOutput.text = ""
try {
val task = async { throwingException() }
addTextToTextView("Ok ${task.await()}")
} catch (e: Exception) {
addTextToTextView("handlingAsyncException caught: ${e}")
}
}
}
Exceptions - async #1
private fun handlingAsyncException1() {
launch {
txtOutput.text = ""
val task = async(SupervisorJob(job)) {
methodThatThrowsException()
}
try {
addTextToTextView("Ok ${task.await()}")
} catch (e: Throwable) {
addTextToTextView("Erro! ${e.message}")
}
}
}
Exceptions - async #2
private fun handlingAsyncException2() {
launch {
txtOutput.text = ""
supervisorScope {
val task = async { methodThatThrowsException() }
try {
addTextToTextView("Ok ${task.await()}")
} catch (e: Throwable) {
addTextToTextView("Erro! ${e.message}")
}
}
}
}
Exceptions - async #3
private fun handlingAsyncException3() {
launch {
txtOutput.text = ""
try {
coroutineScope {
val task = async {
methodThatThrowsException()
}
addTextToTextView("Ok ${task.await()}")
}
} catch (e: Throwable) {
addTextToTextView("Erro! ${e.message}")
}
}
}
Cancelamento
• Para cancelar um job, basta chamar o método cancel.

• Uma vez cancelado o job não pode ser reusado.

• Para cancelar os jobs filhos, use cancelChildren.

• A propriedade isActive indica que o job está em
execução, isCancelled cancelado, e isCompleted
terminou sua execução.

• Se cancelado enquanto suspenso, levanta
CancellationException.
private fun cancelDemo() {
val startTime = System.currentTimeMillis()
longJob = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 20 && isActive) { // this == CoroutineScope, que possui isActive
if (System.currentTimeMillis() >= nextPrintTime) {
Log.d("NGVL","job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
}
private fun nonCancellableJob() {
nonCancellableJob = launch {
withContext(NonCancellable) {
println("job: I'm here")
delay(5000L)
println("job: I'm non-cancellable")
}
}
}
withTimeout
• Executa uma coroutine levantando uma
TimeoutCancellationException	caso sua duração
exceda o timeout especificado.

• Uma vez que o cancelamento é apenas uma exceção, é
possível trata-la facilmente.

• É possível usar a função withTimeoutOrNull que é similar
a withTimeout, mas retorna null ao invés de levantar a
exceção.
withTimeout
private fun timeoutDemo() {
launch {
txtOutput.text = ""
try {
withTimeout(1300L) {
repeat(10) { i ->
withContext(Dispatchers.Default) {
delay(500L)
}
addTextToTextView("I'm sleeping $i ...")
}
}
} catch (e: TimeoutCancellationException) { // opcional
addTextToTextView("Exception! ${e.message}")
}
}
}
withTimeoutOrNull
private fun timeoutOrNullDemo() {
launch {
txtOutput.text = ""
val task = async(Dispatchers.Default) {
delay(5000)
"Done!"
}
// suspend until task is finished or return null in 2 sec
val result = withTimeoutOrNull(2000) { task.await() }
addTextToTextView("Result: $result") // ui thread
}
}
Converting Callbacks to
Coroutines
class LocationManager {
fun getCurrentLocation(callback: (LatLng?) -> Unit) {
// get the location...
callback(LatLng(-8.187,-36.156))
}
}
suspend fun getMyLocation(lm: LocationManager): LatLng {
return suspendCoroutine { continuation ->
lm.getCurrentLocation { latLng ->
if (latLng == null) {
continuation.resumeWithException(
Exception("Fail to get user location")
)
} else {
continuation.resume(latLng)
}
}
}
}
Converting Callbacks to
Coroutines
class LocationManager {
fun getCurrentLocation(callback: (LatLng?) -> Unit) {
// get the location...
callback(LatLng(-8.187,-36.156))
}
}
suspend fun getMyLocation(lm: LocationManager): LatLng {
return suspendCancellableCoroutine { continuation ->
lm.getCurrentLocation { latLng ->
if (latLng == null) {
continuation.resumeWithException(
Exception("Fail to get user location")
)
} else {
continuation.resume(latLng)
}
}
}
}
Nos bastidores, uma suspending function é
convertida pelo compilador para uma função (de
mesmo nome) que recebe um objeto do tipo
Continuation.



fun sum(a: Int, b: Int, Continuation<Int>)
Continuation é uma interface que contém duas funções que
são invocadas para continuar com a execução da coroutine
(normalmente retornando um valor) ou levantar uma exceção
caso algum erro ocorra.

interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Reactive Coroutines
Channel
• Serve basicamente para enviar dados de
uma coroutine para outra.

• O conceito de canal é similar a uma
blocking queue mas utiliza suspending
operations ao invés de blocking operations.
EXPERIMENTAL
2019-06-11 21:08:41.988 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 1
2019-06-11 21:08:41.990 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 4
2019-06-11 21:08:42.005 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 9
2019-06-11 21:08:42.005 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 16
2019-06-11 21:08:42.051 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 25
2019-06-11 21:08:42.051 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: Done!
coroutineScope.launch {
val channel = Channel<Int>()
launch {
// this might be heavy CPU-consuming computation or
// async logic, we’ll just send five squares
for (x in 1..5) channel.send(x * x)
}
// here we print five received integers:
repeat(5) { println(channel.receive()) }
println("Done!")
}
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.ReceiveChannel
class NumberSender {
private var currentValue = 0
private val numberChannel = BroadcastChannel<Int>(10)
fun getChannel(): ReceiveChannel<Int> =
numberChannel.openSubscription()
suspend fun sendNext() {
numberChannel.send(currentValue++)
}
fun close() = numberChannel.close()
}
class ChannelActivity : AppCompatActivity() {
private val sender = NumberSender()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_channel)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getChannel().consumeEach {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
Actors
• Basicamente, um ator é um elemento que
recebe mensagens por meio de um canal e
realiza algum trabalho baseado nelas.

• O retorno dessa função é um objeto do tipo
SendChannel que pode ser usado para
enviar mensagens para essa coroutine.
EXPERIMENTAL
class BooksWithActorViewModel : ViewModel() {
private var _screenState = MutableLiveData<State>()
val screenState: LiveData<State> = _screenState
private val actor: SendChannel<Action> = viewModelScope.actor {
for (action in this) when (action) {
is Action.LoadBooks -> doLoadBooks()
}
}
private suspend fun doLoadBooks() {
try {
_screenState.value = State.LoadingResults(true)
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
_screenState.value = State.BooksResult(books)
} catch (e: Exception) {
_screenState.value = State.ErrorResult(Exception(e))
} finally {
_screenState.value = State.LoadingResults(false)
}
}
fun loadBooksFromWeb() {
actor.offer(Action.LoadBooks)
}
override fun onCleared() {
super.onCleared()
actor.close()
}
}
class ActorDemoActivity : AppCompatActivity() {
private val viewModel: BooksWithActorViewModel by lazy { ... }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_actor_demo)
viewModel.screenState.observe(this, Observer { state ->
when (state) {
is State.LoadingResults -> showLoading(state.isLoading)
is State.BooksResult -> showResults(state.books)
is State.ErrorResult -> showError(state.t)
}
})
btnLoad.setOnClickListener {
viewModel.loadBooksFromWeb()
}
}
private fun showResults(books: List<Book>) {
txtResult.text = ""
books.forEach { txtResult.append("${it.title}n") }
}
private fun showLoading(show: Boolean) {
pbLoading.visibility = if (show) View.VISIBLE else View.GONE
}
private fun showError(t: Throwable) {
Toast.makeText(this, "${t.message}", Toast.LENGTH_SHORT).show()
}
}
Flow
• Flow é uma abstração de um cold stream.

• Nada é executado/emitido qualquer item até que algum
consumidor se registre no fluxo.

• Diversos operadores como no RxJava.
EXPERIMENTAL
@FlowPreview
public interface Flow<out T> {
public suspend fun collect(collector: FlowCollector<T>)
}
@FlowPreview
public interface FlowCollector<in T> {
public suspend fun emit(value: T)
}
private fun flowDemo() {
val intFlow = flow {
for (i in 0 until 10) {
emit(i) //calls emit directly from the body of a FlowCollector
}
}
launch {
txtOutput.text = ""
intFlow.collect { number ->
addTextToTextView("$numbern")
}
addTextToTextView("DONE!")
}
}
private fun flowDemo2() {
launch {
txtOutput.text = ""
(0..100).asFlow()
.map { it * it } // executed in IO
.filter { it % 4 == 0 } //executed in IO
.flowOn(Dispatchers.IO) //changes upstream context, asFlow, map and filter
.map { it * 2 } // not affected, continues in parent context
.flowOn(Dispatchers.Main)
.collect {number ->
addTextToTextView("$numbern")
}
}
}
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
class NumberFlow {
private var currentValue = 0
private val numberChannel = BroadcastChannel<Int>(10)
fun getFlow(): Flow<Int> = numberChannel.asFlow()
suspend fun sendNext() {
numberChannel.send(currentValue++)
}
fun close() = numberChannel.close()
}
class FlowActivity : AppCompatActivity() {
private val sender = NumberFlow()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flow)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getFlow()
.collect {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
Room
Room
dependencies {
def room_version = "2.1.0-rc01"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// 👇 Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
...
}
@Dao
interface UserDao {
@Query("SELECT * FROM user")
suspend fun getAll(): List<User>
@Query("SELECT * FROM user WHERE uid = :id")
suspend fun getUser(id: Long): User
@Insert
suspend fun insert(users: User): Long
@Delete
suspend fun delete(user: User)
}
@Dao
interface UserDao {
@Query("SELECT * FROM user")
suspend fun getAll(): ReceiveChannel<List<User>>
@Query("SELECT * FROM user WHERE uid = :id")
suspend fun getUser(id: Long): ReceiveChannel<User>
@Insert
suspend fun insert(users: User): Long
@Delete
suspend fun delete(user: User)
}
NOT

IMPLEMENTED.

YET?
@Dao
interface UserDao {
@Query("SELECT * FROM user")
suspend fun getAll(): Flow<List<User>>
@Query("SELECT * FROM user WHERE uid = :id")
suspend fun getUser(id: Long): Flow<User>
@Insert
suspend fun insert(users: User): Long
@Delete
suspend fun delete(user: User)
}
NOT

IMPLEMENTED.

YET?
LiveData
LiveData
dependencies {
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01"
}
val users: LiveData<List<User>> = liveData {
val data = dao.getAll()
emit(data)
}
...
users.observe(this) { users ->
users.forEach { Log.d("NGVL", it.toString()) }
}
LiveData
@Dao
interface UserDao {
...
@Query("SELECT * FROM user")
fun getLiveAll(): LiveData<List<User>>
}
val users: LiveData<List<User>> = liveData {
val data = dao.getLiveAll()
emitSource(data)
}
...

users.observe(this) { users ->
users.forEach { Log.d("NGVL", it.toString()) }
}
Conclusão
• Coroutines vêm se tornando a forma de padrão para
realizar código assíncrono no Android.

• Essa é uma recomendação do Google.

• Além do Jetpack, outras bibliotecas estão migrando pro
Jetpack (ex: Retrofit).

• Muita atenção para as APIs experimentais de hoje. Elas
podem ser o seu código de amanhã!
Referências #1
• Android Suspenders (Android Dev Summit 2018)

https://www.youtube.com/watch?v=EOjq4OIWKqM

• Understand Kotlin Coroutines on Android (Google I/O 2019)

https://www.youtube.com/watch?v=BOHK_w09pVA

• Coroutines Guide

https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-
guide.md

• Android Suspenders by Chris Banes (KotlinConf 2018)

https://www.youtube.com/watch?v=P7ov_r1JZ1g

• Room & Coroutines (Florina Muntenescu)

https://medium.com/androiddevelopers/room-coroutines-422b786dc4c5
Referências #2
• Using Kotlin Coroutines in your Android App

https://codelabs.developers.google.com/codelabs/kotlin-coroutines

• Use Kotlin coroutines with Architecture Components

https://developer.android.com/topic/libraries/architecture/coroutines

• Create a Clean-Code App with Kotlin Coroutines and Android Architecture
Components

https://blog.elpassion.com/create-a-clean-code-app-with-kotlin-coroutines-
and-android-architecture-components-f533b04b5431

• Android Coroutine Recipes (Dmytro Danylyk)

https://proandroiddev.com/android-coroutine-recipes-33467a4302e9

• Kotlin Coroutines patterns & anti-patterns

https://proandroiddev.com/kotlin-coroutines-patterns-anti-patterns-
f9d12984c68e
Referências #3
• The reason to avoid GlobalScope (Roman Elizarov)

https://medium.com/@elizarov/the-reason-to-avoid-globalscope-835337445abc
• WorkManager meets Kotlin (Pietro Maggi)

https://medium.com/androiddevelopers/workmanager-meets-kotlin-
b9ad02f7405e

• Coroutine Context and Scope (Roman Elizarov)

https://medium.com/@elizarov/coroutine-context-and-scope-c8b255d59055

• Easy Coroutines in Android: viewModelScope (Manuel Vivo)

https://medium.com/androiddevelopers/easy-coroutines-in-android-
viewmodelscope-25bffb605471

• Exceed the Android Speed Limit

https://medium.com/androiddevelopers/exceed-the-android-speed-limit-
b73a0692abc1
Referências #4
• An Early look at Kotlin Coroutine’s Flow

https://proandroiddev.com/an-early-look-at-kotlin-coroutines-
flow-62e46baa6eb0

• Coroutines on Android (Sean McQuillan)

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-
the-background-3e0e54d20bb
• Kotlin Flows and Coroutines (Roman Elizarov)

https://medium.com/@elizarov/kotlin-flows-and-coroutines-256260fb3bdb
• Simple design of Kotlin Flow (Roman Elizarov)

https://medium.com/@elizarov/simple-design-of-kotlin-flow-4725e7398c4c
• React Streams and Kotlin Flows (Roman Elizarov)

https://medium.com/@elizarov/reactive-streams-and-kotlin-flows-
bfd12772cda4
Obrigado!
@nglauber
www.nglauber.com.br

youtube.com/nglauber
slideshare.com/nglauber

Mais conteúdo relacionado

Mais procurados

rx.js make async programming simpler
rx.js make async programming simplerrx.js make async programming simpler
rx.js make async programming simpler
Alexander Mostovenko
 
Snapshot clone-boot-presentation-final
Snapshot clone-boot-presentation-finalSnapshot clone-boot-presentation-final
Snapshot clone-boot-presentation-final
Open Stack
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}
.toster
 

Mais procurados (20)

Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2
 
Writing native bindings to node.js in C++
Writing native bindings to node.js in C++Writing native bindings to node.js in C++
Writing native bindings to node.js in C++
 
Cascadia.js: Don't Cross the Streams
Cascadia.js: Don't Cross the StreamsCascadia.js: Don't Cross the Streams
Cascadia.js: Don't Cross the Streams
 
rx.js make async programming simpler
rx.js make async programming simplerrx.js make async programming simpler
rx.js make async programming simpler
 
RDSDataSource: Мастер-класс по Dip
RDSDataSource: Мастер-класс по DipRDSDataSource: Мастер-класс по Dip
RDSDataSource: Мастер-класс по Dip
 
Snapshot clone-boot-presentation-final
Snapshot clone-boot-presentation-finalSnapshot clone-boot-presentation-final
Snapshot clone-boot-presentation-final
 
Ansible fest Presentation slides
Ansible fest Presentation slidesAnsible fest Presentation slides
Ansible fest Presentation slides
 
Lua: the world's most infuriating language
Lua: the world's most infuriating languageLua: the world's most infuriating language
Lua: the world's most infuriating language
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsPlaying With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.js
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}
 
Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016
 
Rethink Async With RXJS
Rethink Async With RXJSRethink Async With RXJS
Rethink Async With RXJS
 
PuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with PuppetPuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with Puppet
 
HAB Software Woes
HAB Software WoesHAB Software Woes
HAB Software Woes
 
Async Frontiers
Async FrontiersAsync Frontiers
Async Frontiers
 
rake puppetexpert:create - Puppet Camp Silicon Valley 2014
rake puppetexpert:create - Puppet Camp Silicon Valley 2014rake puppetexpert:create - Puppet Camp Silicon Valley 2014
rake puppetexpert:create - Puppet Camp Silicon Valley 2014
 
Automating Kubernetes Environments with Ansible
Automating Kubernetes Environments with AnsibleAutomating Kubernetes Environments with Ansible
Automating Kubernetes Environments with Ansible
 
Developing JavaScript Widgets
Developing JavaScript WidgetsDeveloping JavaScript Widgets
Developing JavaScript Widgets
 
303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code
 
Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with Jasmine
 

Semelhante a Aplicações assíncronas no Android com Coroutines & Jetpack

CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
Sven Haiges
 
OpenStack Horizon: Controlling the Cloud using Django
OpenStack Horizon: Controlling the Cloud using DjangoOpenStack Horizon: Controlling the Cloud using Django
OpenStack Horizon: Controlling the Cloud using Django
David Lapsley
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
偉格 高
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
David Padbury
 

Semelhante a Aplicações assíncronas no Android com Coroutines & Jetpack (20)

Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
Emerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the HorizonEmerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the Horizon
 
droidcon Transylvania - Kotlin Coroutines
droidcon Transylvania - Kotlin Coroutinesdroidcon Transylvania - Kotlin Coroutines
droidcon Transylvania - Kotlin Coroutines
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
 
OpenStack Horizon: Controlling the Cloud using Django
OpenStack Horizon: Controlling the Cloud using DjangoOpenStack Horizon: Controlling the Cloud using Django
OpenStack Horizon: Controlling the Cloud using Django
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Swift - One step forward from Obj-C
Swift -  One step forward from Obj-CSwift -  One step forward from Obj-C
Swift - One step forward from Obj-C
 
mobl
moblmobl
mobl
 
Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!
 
Introduction to Protractor
Introduction to ProtractorIntroduction to Protractor
Introduction to Protractor
 
CS101- Introduction to Computing- Lecture 35
CS101- Introduction to Computing- Lecture 35CS101- Introduction to Computing- Lecture 35
CS101- Introduction to Computing- Lecture 35
 
Cloud Orchestration with RightScale Cloud Workflow
Cloud Orchestration with RightScale Cloud WorkflowCloud Orchestration with RightScale Cloud Workflow
Cloud Orchestration with RightScale Cloud Workflow
 
Kotlin - Coroutine
Kotlin - CoroutineKotlin - Coroutine
Kotlin - Coroutine
 
Kotlin from-scratch 3 - coroutines
Kotlin from-scratch 3 - coroutinesKotlin from-scratch 3 - coroutines
Kotlin from-scratch 3 - coroutines
 
Kotlin coroutine - the next step for RxJava developer?
Kotlin coroutine - the next step for RxJava developer?Kotlin coroutine - the next step for RxJava developer?
Kotlin coroutine - the next step for RxJava developer?
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomy
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 

Mais de Nelson Glauber Leal

Mais de Nelson Glauber Leal (20)

Seu primeiro app Android e iOS com Compose Multiplatform
Seu primeiro app Android e iOS com Compose MultiplatformSeu primeiro app Android e iOS com Compose Multiplatform
Seu primeiro app Android e iOS com Compose Multiplatform
 
Desenvolvimento Moderno de Aplicações Android 2023
Desenvolvimento Moderno de Aplicações Android 2023Desenvolvimento Moderno de Aplicações Android 2023
Desenvolvimento Moderno de Aplicações Android 2023
 
Novidades incríveis do Android em 2023
Novidades incríveis do Android em 2023Novidades incríveis do Android em 2023
Novidades incríveis do Android em 2023
 
Novidades das Bibliotecas Jetpack do Android (2021)
Novidades das Bibliotecas Jetpack do Android (2021)Novidades das Bibliotecas Jetpack do Android (2021)
Novidades das Bibliotecas Jetpack do Android (2021)
 
Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021
 
Jetpack Compose a new way to implement UI on Android
Jetpack Compose a new way to implement UI on AndroidJetpack Compose a new way to implement UI on Android
Jetpack Compose a new way to implement UI on Android
 
Jetpack Compose a nova forma de implementar UI no Android
Jetpack Compose a nova forma de implementar UI no AndroidJetpack Compose a nova forma de implementar UI no Android
Jetpack Compose a nova forma de implementar UI no Android
 
O que é preciso para ser um desenvolvedor Android
O que é preciso para ser um desenvolvedor AndroidO que é preciso para ser um desenvolvedor Android
O que é preciso para ser um desenvolvedor Android
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard Library
 
Persisting Data on SQLite using Room
Persisting Data on SQLite using RoomPersisting Data on SQLite using Room
Persisting Data on SQLite using Room
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
 
Desenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de Aplicativos AndroidDesenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de Aplicativos Android
 
Desenvolvimento Moderno de aplicativos Android
Desenvolvimento Moderno de aplicativos AndroidDesenvolvimento Moderno de aplicativos Android
Desenvolvimento Moderno de aplicativos Android
 
Turbinando o desenvolvimento Android com Kotlin
Turbinando o desenvolvimento Android com KotlinTurbinando o desenvolvimento Android com Kotlin
Turbinando o desenvolvimento Android com Kotlin
 
Tudo que você precisa saber sobre Constraint Layout
Tudo que você precisa saber sobre Constraint LayoutTudo que você precisa saber sobre Constraint Layout
Tudo que você precisa saber sobre Constraint Layout
 
Persistência de Dados no SQLite com Room
Persistência de Dados no SQLite com RoomPersistência de Dados no SQLite com Room
Persistência de Dados no SQLite com Room
 
The world of Android Animations
The world of Android AnimationsThe world of Android Animations
The world of Android Animations
 
Android Constraint Layout
Android Constraint LayoutAndroid Constraint Layout
Android Constraint Layout
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no Android
 

Último

%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 

Último (20)

Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 

Aplicações assíncronas no Android com Coroutines & Jetpack

  • 1. Coroutines & Jetpack Aplicações assíncronas no Android com Nelson Glauber @nglauber
 slideshare.com/nglauber
 youtube.com/nglauber
  • 2. A main thread do Android • Carregar arquivo de layout • Desenhar as views • Tratar os eventos de UI • …
  • 3. A main thread do Android
  • 4. A main thread do Android
  • 6. Async no Android • AsyncTask ! • Thread + Handler " • Loaders (deprecated) ! • Volley #$ • RxJava 🧐
  • 7. Coroutines • Essencialmente, coroutines são light-weight threads. • Fácil de usar (sem mais "callbacks hell” e/ou centenas de operadores). • Úteis para operações de I/O ou qualquer outra tarefa computacional mais onerosa. • Permite a substituição de callbacks por operações assíncronas.
  • 8. Dependências dependencies { def coroutines_version = '1.2.1' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" ... }
  • 9. suspend • Suspending functions são o centro de tudo em Coroutines. Elas são basicamente funções que podem ser pausadas e retomadas após algum tempo. • Podem executar longas tarefas e aguardar o resultado sem bloquear a thread atual. • A sintaxe é idêntica a uma função “normal”, exceto pela adição da palavra reservada suspend. • Por si só, uma suspending function não é assíncrona. • Só pode ser chamada a partir de outra suspending function.
  • 10. import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.Assert.assertEquals class CalculatorUnitTest { @Test fun sum_isCorrect() = runBlocking { val calc = Calculator() assertEquals(4, calc.sum(2, 2)) } } import kotlinx.coroutines.delay class Calculator { suspend fun sum(a: Int, b: Int): Int { delay(5_000) return a + b } }
  • 11. Coroutines no Android • Para entendermos melhor as Coroutines no Android precisamos falar de 4 conceitos: • Job • Context • Scope • Dispatcher
  • 12. class MainActivity : AppCompatActivity() { private val job = Job() private val coroutineScope = CoroutineScope(job + Dispatchers.Main) override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ...
  • 13. class MainActivity : AppCompatActivity() { private val job = Job() private val coroutineScope = CoroutineScope(job + Dispatchers.Main) override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ... Job • Um Job representa uma tarefa ou conjunto de tarefas em background. • A função launch retorna um Job. • Mantém a referência do código que está em execução. • Pode possuir “filhos”. • Pode ser cancelado usando a função cancel.
  • 14. class MainActivity : AppCompatActivity() { private val job = Job() private val coroutineScope = CoroutineScope(job + Dispatchers.Main) override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ... Context • É um conjunto de atributos que configuram uma coroutine. • Representada pela interface CoroutineContext. • Pode definir a política de threading, tratamento de exceções, etc.
  • 15. class MainActivity : AppCompatActivity() { private val job = Job() private val coroutineScope = CoroutineScope(job + Dispatchers.Main) override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ... Scope • Um escopo serve como uma espécie de ciclo de vida para um conjunto de coroutines. • Coroutines sempre rodam em um escopo. • Permite um maior controle das tarefas em execução
  • 16. GlobalScope • O GlobalScope, como o nome sugere, é utilizado em tarefas cujo o escopo é global da aplicação. • O GlobalScope é considerado um anti-pattern no Android e deve ser evitado. private suspend fun loadName(): String { delay(5000) return "Glauber" } private fun firstCoroutine() { GlobalScope.launch { val name = loadName() println("$name!") } println("Hello, ") }
  • 17. class MainActivity : AppCompatActivity() { private val job = Job() private val coroutineScope = CoroutineScope(job + Dispatchers.Main) override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ... Dispatcher • Um dispatcher define o pool de threads que a coroutine executará. • Default - É otimizado para processos que usam a CPU mais intensamente. • IO - recomendado para tarefas de rede ou arquivos. O pool de threads é compartilhado com o dispatcher DEFAULT. • Main - main thread do Android.
  • 18. fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { addTextToTextView("${it.title}n") } } } &
  • 19. fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = BookHttp.loadBooks() books?.forEach { txtOutput.append("${it.title}n") } } } android.os.NetworkOnMainThreadException at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1513) at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:117) at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105) at java.net.InetAddress.getAllByName(InetAddress.java:1154) at com.android.okhttp.Dns$1.lookup(Dns.java:39)
  • 20. FATAL EXCEPTION: DefaultDispatcher-worker-1 Process: br.com.nglauber.coroutinesdemo, PID: 26507 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225) fun callWebService() { coroutineScope.launch(Dispatchers.IO){ txtOutput.text = "" val books = BookHttp.loadBooks() books?.forEach { txtOutput.append("${it.title}n") } } }
  • 21. class MainActivity : AppCompatActivity() { private val job = Job() private val coroutineScope = CoroutineScope(job + Dispatchers.Main) override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ...
  • 22. class MainActivity : AppCompatActivity(), CoroutineScope { private val job = Job() override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job override fun onDestroy() { super.onDestroy() job.cancel() } fun callWebService() { launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } books?.forEach { txtOutput.append("${it.title}n") } } } ...
  • 24. Lifecycle Scope • Escopo atrelado ao ciclo de vida da Activity, Fragment ou View do Fragment • Além da função launch, podemos usar o launchWhenCreated, launchWhenStarted e launchWhenResumed. dependencies { ... implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01" }
  • 25. class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... lifecycleScope.launch { ... } // or... lifecycleScope.launchWhenCreated { ... } // or... lifecycleScope.launchWhenStarted { ... } }
  • 26. class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... lifecycleScope.launch { ... } // or... lifecycleScope.launchWhenCreated { ... } // or... lifecycleScope.launchWhenStarted { ... } }
  • 27. class MyFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewLifecycleOwner.lifecycleScope.launch { ... } } }
  • 29. ViewModel Scope 
 
 Um ViewModel agora possui a propriedade viewModelScope. dependencies { implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01" testImplementation "androidx.arch.core:core-testing:2.1.0-beta01" }
  • 30. class MainViewModel : ViewModel() { private val _message = MutableLiveData<String>() val message: LiveData<String> = _message fun loadMessage() { viewModelScope.launch { val message = withContext(Dispatchers.IO) { loadMessageFromNetwork() } _message.value = message } } private suspend fun loadMessageFromNetwork() : String { delay(2_000) return "Hello from ViewModel" } }
  • 31. class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ... viewModel.message.observe(this, Observer { s -> txtOutput.text = s }) viewModel.loadMessage() } ...
  • 33. WorkManager dependencies { def work_version = "2.1.0-beta02" implementation "androidx.work:work-runtime-ktx:$work_version" androidTestImplementation "androidx.work:work-testing:$work_version" }
  • 34. class MyWork(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result = try { val output = inputData.run { val x = getInt("x", 0) val y = getInt("y", 0) val result = Calculator().sum(x, y) workDataOf("result" to result) } Result.success(output) } catch (error: Throwable) { Result.failure() } } private fun callMyWork() { val request = OneTimeWorkRequestBuilder<MyWork>() .setInputData(workDataOf("x" to 84, "y" to 12)) .build() WorkManager.getInstance(this).run { enqueue(request) getWorkInfoByIdLiveData(request.id) .observe(this@MainActivity, Observer { if (it.state == WorkInfo.State.SUCCEEDED) { val result = it.outputData.getInt("result", 0) addTextToTextView("Result-> $result") } }) }
  • 36. Iniciando uma coroutine • As duas formas de iniciar uma coroutine são: • A função launch é uma “fire and forget”  que significa que não retornará o resultado para que a chamou (mas retornará um Job). • A função async retorna um objeto Deferred que permite obter o seu resultado.
  • 37. launch = Sequencial private fun sequentialCalls() { txtOutput.text = "" launch { val time = measureTimeMillis { val one = doSomethingUsefulOne() val two = doSomethingUsefulTwo() addTextToTextView("The answer is ${one + two}") } addTextToTextView("Completed in $time ms") } } The answer is 42
 Completed in 2030 ms
  • 38. async = Paralelo private fun parallelCalls() { txtOutput.text = "" launch { val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } addTextToTextView("The answer is ${one.await() + two.await()} ") } addTextToTextView("Completed in $time ms") } } The answer is 42
 Completed in 1038 ms
  • 39. Launch x Async Em quase todos os casos, deve-se usar a função launch para iniciar uma coroutine a partir de uma função “normal”. Uma função comum não pode chamar o await (porque ela é uma suspending function), então não faz muito sentido usar o async como ponto de partida.
  • 40. Exceptions • As exceções tem comportamentos diferentes para o launch e o async. 
  • 41. Exceptions - launch private fun handlingException() { launch { txtOutput.text = "" try { val a = methodThatThrowsException() addTextToTextView("Ok ${a}") } catch (e: Exception) { addTextToTextView("handlingException caught: ${e.message}") } } }
  • 42. Exceptions - async private fun handlingAsyncException() { launch { txtOutput.text = "" try { val task = async { throwingException() } addTextToTextView("Ok ${task.await()}") } catch (e: Exception) { addTextToTextView("handlingAsyncException caught: ${e}") } } }
  • 43. Exceptions - async #1 private fun handlingAsyncException1() { launch { txtOutput.text = "" val task = async(SupervisorJob(job)) { methodThatThrowsException() } try { addTextToTextView("Ok ${task.await()}") } catch (e: Throwable) { addTextToTextView("Erro! ${e.message}") } } }
  • 44. Exceptions - async #2 private fun handlingAsyncException2() { launch { txtOutput.text = "" supervisorScope { val task = async { methodThatThrowsException() } try { addTextToTextView("Ok ${task.await()}") } catch (e: Throwable) { addTextToTextView("Erro! ${e.message}") } } } }
  • 45. Exceptions - async #3 private fun handlingAsyncException3() { launch { txtOutput.text = "" try { coroutineScope { val task = async { methodThatThrowsException() } addTextToTextView("Ok ${task.await()}") } } catch (e: Throwable) { addTextToTextView("Erro! ${e.message}") } } }
  • 46. Cancelamento • Para cancelar um job, basta chamar o método cancel. • Uma vez cancelado o job não pode ser reusado. • Para cancelar os jobs filhos, use cancelChildren. • A propriedade isActive indica que o job está em execução, isCancelled cancelado, e isCompleted terminou sua execução. • Se cancelado enquanto suspenso, levanta CancellationException.
  • 47. private fun cancelDemo() { val startTime = System.currentTimeMillis() longJob = launch(Dispatchers.Default) { var nextPrintTime = startTime var i = 0 while (i < 20 && isActive) { // this == CoroutineScope, que possui isActive if (System.currentTimeMillis() >= nextPrintTime) { Log.d("NGVL","job: I'm sleeping ${i++} ...") nextPrintTime += 500L } } } }
  • 48. private fun nonCancellableJob() { nonCancellableJob = launch { withContext(NonCancellable) { println("job: I'm here") delay(5000L) println("job: I'm non-cancellable") } } }
  • 49. withTimeout • Executa uma coroutine levantando uma TimeoutCancellationException caso sua duração exceda o timeout especificado. • Uma vez que o cancelamento é apenas uma exceção, é possível trata-la facilmente. • É possível usar a função withTimeoutOrNull que é similar a withTimeout, mas retorna null ao invés de levantar a exceção.
  • 50. withTimeout private fun timeoutDemo() { launch { txtOutput.text = "" try { withTimeout(1300L) { repeat(10) { i -> withContext(Dispatchers.Default) { delay(500L) } addTextToTextView("I'm sleeping $i ...") } } } catch (e: TimeoutCancellationException) { // opcional addTextToTextView("Exception! ${e.message}") } } }
  • 51. withTimeoutOrNull private fun timeoutOrNullDemo() { launch { txtOutput.text = "" val task = async(Dispatchers.Default) { delay(5000) "Done!" } // suspend until task is finished or return null in 2 sec val result = withTimeoutOrNull(2000) { task.await() } addTextToTextView("Result: $result") // ui thread } }
  • 52. Converting Callbacks to Coroutines class LocationManager { fun getCurrentLocation(callback: (LatLng?) -> Unit) { // get the location... callback(LatLng(-8.187,-36.156)) } } suspend fun getMyLocation(lm: LocationManager): LatLng { return suspendCoroutine { continuation -> lm.getCurrentLocation { latLng -> if (latLng == null) { continuation.resumeWithException( Exception("Fail to get user location") ) } else { continuation.resume(latLng) } } } }
  • 53. Converting Callbacks to Coroutines class LocationManager { fun getCurrentLocation(callback: (LatLng?) -> Unit) { // get the location... callback(LatLng(-8.187,-36.156)) } } suspend fun getMyLocation(lm: LocationManager): LatLng { return suspendCancellableCoroutine { continuation -> lm.getCurrentLocation { latLng -> if (latLng == null) { continuation.resumeWithException( Exception("Fail to get user location") ) } else { continuation.resume(latLng) } } } }
  • 54. Nos bastidores, uma suspending function é convertida pelo compilador para uma função (de mesmo nome) que recebe um objeto do tipo Continuation.
 
 fun sum(a: Int, b: Int, Continuation<Int>) Continuation é uma interface que contém duas funções que são invocadas para continuar com a execução da coroutine (normalmente retornando um valor) ou levantar uma exceção caso algum erro ocorra.
 interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  • 56. Channel • Serve basicamente para enviar dados de uma coroutine para outra. • O conceito de canal é similar a uma blocking queue mas utiliza suspending operations ao invés de blocking operations. EXPERIMENTAL
  • 57. 2019-06-11 21:08:41.988 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 1 2019-06-11 21:08:41.990 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 4 2019-06-11 21:08:42.005 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 9 2019-06-11 21:08:42.005 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 16 2019-06-11 21:08:42.051 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: 25 2019-06-11 21:08:42.051 5545-5545/br.com.nglauber.coroutinesdemo I/System.out: Done! coroutineScope.launch { val channel = Channel<Int>() launch { // this might be heavy CPU-consuming computation or // async logic, we’ll just send five squares for (x in 1..5) channel.send(x * x) } // here we print five received integers: repeat(5) { println(channel.receive()) } println("Done!") }
  • 58. import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.ReceiveChannel class NumberSender { private var currentValue = 0 private val numberChannel = BroadcastChannel<Int>(10) fun getChannel(): ReceiveChannel<Int> = numberChannel.openSubscription() suspend fun sendNext() { numberChannel.send(currentValue++) } fun close() = numberChannel.close() }
  • 59. class ChannelActivity : AppCompatActivity() { private val sender = NumberSender() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_channel) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getChannel().consumeEach { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 60. Actors • Basicamente, um ator é um elemento que recebe mensagens por meio de um canal e realiza algum trabalho baseado nelas. • O retorno dessa função é um objeto do tipo SendChannel que pode ser usado para enviar mensagens para essa coroutine. EXPERIMENTAL
  • 61. class BooksWithActorViewModel : ViewModel() { private var _screenState = MutableLiveData<State>() val screenState: LiveData<State> = _screenState private val actor: SendChannel<Action> = viewModelScope.actor { for (action in this) when (action) { is Action.LoadBooks -> doLoadBooks() } } private suspend fun doLoadBooks() { try { _screenState.value = State.LoadingResults(true) val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } _screenState.value = State.BooksResult(books) } catch (e: Exception) { _screenState.value = State.ErrorResult(Exception(e)) } finally { _screenState.value = State.LoadingResults(false) } } fun loadBooksFromWeb() { actor.offer(Action.LoadBooks) } override fun onCleared() { super.onCleared() actor.close() } }
  • 62. class ActorDemoActivity : AppCompatActivity() { private val viewModel: BooksWithActorViewModel by lazy { ... } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_actor_demo) viewModel.screenState.observe(this, Observer { state -> when (state) { is State.LoadingResults -> showLoading(state.isLoading) is State.BooksResult -> showResults(state.books) is State.ErrorResult -> showError(state.t) } }) btnLoad.setOnClickListener { viewModel.loadBooksFromWeb() } } private fun showResults(books: List<Book>) { txtResult.text = "" books.forEach { txtResult.append("${it.title}n") } } private fun showLoading(show: Boolean) { pbLoading.visibility = if (show) View.VISIBLE else View.GONE } private fun showError(t: Throwable) { Toast.makeText(this, "${t.message}", Toast.LENGTH_SHORT).show() } }
  • 63. Flow • Flow é uma abstração de um cold stream. • Nada é executado/emitido qualquer item até que algum consumidor se registre no fluxo. • Diversos operadores como no RxJava. EXPERIMENTAL
  • 64. @FlowPreview public interface Flow<out T> { public suspend fun collect(collector: FlowCollector<T>) } @FlowPreview public interface FlowCollector<in T> { public suspend fun emit(value: T) }
  • 65. private fun flowDemo() { val intFlow = flow { for (i in 0 until 10) { emit(i) //calls emit directly from the body of a FlowCollector } } launch { txtOutput.text = "" intFlow.collect { number -> addTextToTextView("$numbern") } addTextToTextView("DONE!") } }
  • 66. private fun flowDemo2() { launch { txtOutput.text = "" (0..100).asFlow() .map { it * it } // executed in IO .filter { it % 4 == 0 } //executed in IO .flowOn(Dispatchers.IO) //changes upstream context, asFlow, map and filter .map { it * 2 } // not affected, continues in parent context .flowOn(Dispatchers.Main) .collect {number -> addTextToTextView("$numbern") } } }
  • 67. import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow class NumberFlow { private var currentValue = 0 private val numberChannel = BroadcastChannel<Int>(10) fun getFlow(): Flow<Int> = numberChannel.asFlow() suspend fun sendNext() { numberChannel.send(currentValue++) } fun close() = numberChannel.close() }
  • 68. class FlowActivity : AppCompatActivity() { private val sender = NumberFlow() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_flow) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getFlow() .collect { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 69. Room
  • 70. Room dependencies { def room_version = "2.1.0-rc01" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" // 👇 Kotlin Extensions and Coroutines support for Room implementation "androidx.room:room-ktx:$room_version" ... }
  • 71. @Dao interface UserDao { @Query("SELECT * FROM user") suspend fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid = :id") suspend fun getUser(id: Long): User @Insert suspend fun insert(users: User): Long @Delete suspend fun delete(user: User) }
  • 72. @Dao interface UserDao { @Query("SELECT * FROM user") suspend fun getAll(): ReceiveChannel<List<User>> @Query("SELECT * FROM user WHERE uid = :id") suspend fun getUser(id: Long): ReceiveChannel<User> @Insert suspend fun insert(users: User): Long @Delete suspend fun delete(user: User) } NOT
 IMPLEMENTED.
 YET?
  • 73. @Dao interface UserDao { @Query("SELECT * FROM user") suspend fun getAll(): Flow<List<User>> @Query("SELECT * FROM user WHERE uid = :id") suspend fun getUser(id: Long): Flow<User> @Insert suspend fun insert(users: User): Long @Delete suspend fun delete(user: User) } NOT
 IMPLEMENTED.
 YET?
  • 75. LiveData dependencies { implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01" } val users: LiveData<List<User>> = liveData { val data = dao.getAll() emit(data) } ... users.observe(this) { users -> users.forEach { Log.d("NGVL", it.toString()) } }
  • 76. LiveData @Dao interface UserDao { ... @Query("SELECT * FROM user") fun getLiveAll(): LiveData<List<User>> } val users: LiveData<List<User>> = liveData { val data = dao.getLiveAll() emitSource(data) } ...
 users.observe(this) { users -> users.forEach { Log.d("NGVL", it.toString()) } }
  • 77. Conclusão • Coroutines vêm se tornando a forma de padrão para realizar código assíncrono no Android. • Essa é uma recomendação do Google. • Além do Jetpack, outras bibliotecas estão migrando pro Jetpack (ex: Retrofit). • Muita atenção para as APIs experimentais de hoje. Elas podem ser o seu código de amanhã!
  • 78. Referências #1 • Android Suspenders (Android Dev Summit 2018)
 https://www.youtube.com/watch?v=EOjq4OIWKqM • Understand Kotlin Coroutines on Android (Google I/O 2019)
 https://www.youtube.com/watch?v=BOHK_w09pVA • Coroutines Guide
 https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines- guide.md • Android Suspenders by Chris Banes (KotlinConf 2018)
 https://www.youtube.com/watch?v=P7ov_r1JZ1g • Room & Coroutines (Florina Muntenescu)
 https://medium.com/androiddevelopers/room-coroutines-422b786dc4c5
  • 79. Referências #2 • Using Kotlin Coroutines in your Android App
 https://codelabs.developers.google.com/codelabs/kotlin-coroutines • Use Kotlin coroutines with Architecture Components
 https://developer.android.com/topic/libraries/architecture/coroutines • Create a Clean-Code App with Kotlin Coroutines and Android Architecture Components
 https://blog.elpassion.com/create-a-clean-code-app-with-kotlin-coroutines- and-android-architecture-components-f533b04b5431 • Android Coroutine Recipes (Dmytro Danylyk)
 https://proandroiddev.com/android-coroutine-recipes-33467a4302e9 • Kotlin Coroutines patterns & anti-patterns
 https://proandroiddev.com/kotlin-coroutines-patterns-anti-patterns- f9d12984c68e
  • 80. Referências #3 • The reason to avoid GlobalScope (Roman Elizarov)
 https://medium.com/@elizarov/the-reason-to-avoid-globalscope-835337445abc • WorkManager meets Kotlin (Pietro Maggi)
 https://medium.com/androiddevelopers/workmanager-meets-kotlin- b9ad02f7405e • Coroutine Context and Scope (Roman Elizarov)
 https://medium.com/@elizarov/coroutine-context-and-scope-c8b255d59055 • Easy Coroutines in Android: viewModelScope (Manuel Vivo)
 https://medium.com/androiddevelopers/easy-coroutines-in-android- viewmodelscope-25bffb605471 • Exceed the Android Speed Limit
 https://medium.com/androiddevelopers/exceed-the-android-speed-limit- b73a0692abc1
  • 81. Referências #4 • An Early look at Kotlin Coroutine’s Flow
 https://proandroiddev.com/an-early-look-at-kotlin-coroutines- flow-62e46baa6eb0 • Coroutines on Android (Sean McQuillan)
 https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting- the-background-3e0e54d20bb • Kotlin Flows and Coroutines (Roman Elizarov)
 https://medium.com/@elizarov/kotlin-flows-and-coroutines-256260fb3bdb • Simple design of Kotlin Flow (Roman Elizarov)
 https://medium.com/@elizarov/simple-design-of-kotlin-flow-4725e7398c4c • React Streams and Kotlin Flows (Roman Elizarov)
 https://medium.com/@elizarov/reactive-streams-and-kotlin-flows- bfd12772cda4