SlideShare a Scribd company logo
Aplicações assíncronas no Android com

Coroutines & Jetpack
Nelson Glauber
@nglauber
• Carregar arquivo de layout
• Desenhar as views
• Tratar os eventos de UI
• …
A main thread do Android
• O processamento desses eventos deve ocorrer em:
• Menos de 16ms para devices com taxas de atualização
de 60Hz
• Menos de 12ms para dispositivos com taxas de 90Hz
• Menos de < 8ms para dispositivos com taxas de 120Hz
A main thread do Android
A main thread do Android
A main thread do Android
Solução?
• AsyncTask !
• Thread + Handler "
• Loaders (deprecated) !
• Volley #$
• RxJava 🧐
Async no Android
Coroutines
• Essencialmente, coroutines são light-weight threads.
• Fácil de usar (sem mais “callbacks hell” e/ou centenas de
operadores).
• Úteis para qualquer tarefa computacional mais onerosa
(como operações de I/O).
• Permite a substituição de callbacks por operações
assíncronas.
Coroutines
Dependências
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"
...
}
• Suspending functions são o centro de tudo em Coroutines.

• São 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.
suspend
import kotlinx.coroutines.delay
class Calculator {
suspend fun sum(a: Int, b: Int): Int {
delay(5_000)
return a + b
}
}
import kotlinx.coroutines.delay
class Calculator {
suspend fun sum(a: Int, b: Int): Int {
delay(5_000)
return a + b
}
}
import kotlinx.coroutines.runBlocking
import org.junit.*
class CalculatorUnitTest {
@Test
fun sum_isCorrect() = runBlocking {
val calc = Calculator()
assertEquals(4, calc.sum(2, 2))
}
}
• Job
• Context
• Scope
• Dispatcher
Coroutines no Android
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()
}
// update the UI using books
}
}
...
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()
}
// update the UI using books
}
}
...
Job
• Um Job representa uma
tarefa ou conjunto de tarefas
em execução.
• Pode possuir “filhos”.
• A função launch retorna um
Job.
• 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()
}
// update the UI using books
}
}
...
Context
• A interface
CoroutineContext
Representa o conjunto de
atributos que configuram
uma coroutine.
• 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()
}
// update the UI using books
}
}
...
Scope
• Coroutines sempre rodam
em um escopo.
• Serve como uma espécie de
ciclo de vida para um
conjunto de coroutines.
• Permite um maior controle
das tarefas em execução.
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()
}
// update the UI using books
}
}
...
Dispatcher
• Define o pool de threads 

onde a coroutine executará.
• Default: para processos 

que usam a CPU mais
intensamente.
• IO: para tarefas de rede 

ou arquivos. O pool de
threads é compartilhado
com o dispatcher
DEFAULT.
• Main - main thread do
Android.
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()
}
// update the UI using books
}
}
...
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()
}
// update the UI using books
}
}
...
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
// update the UI using books
}
}
fun callWebService() {
coroutineScope.launch {
txtOutput.text = ""
val books = BookHttp.loadBooks()
// update the UI using books
}
}
	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)
fun callWebService() {
coroutineScope.launch(Dispatchers.IO){
txtOutput.text = ""
val books = BookHttp.loadBooks()
// update the UI using books
}
}
	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 {
txtOutput.text = ""
val books = withContext(Dispatchers.IO) {
BookHttp.loadBooks()
}
// update the UI using books
}
}
&
• Suspending functions
• Job
• Context
• Scope
• Dispatcher
Coroutines
Lifecycle
• 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.
Lifecycle Scope
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-beta01"
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
...
}
lifecycleScope.launchWhenCreated {
...
}
lifecycleScope.launchWhenStarted {
...
}
lifecycleScope.launchWhenResumed {
...
}
}
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
...
}
lifecycleScope.launchWhenCreated {
...
}
lifecycleScope.launchWhenStarted {
...
}
lifecycleScope.launchWhenResumed {
...
}
}
}
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
...
}
lifecycleScope.launchWhenCreated {
...
}
lifecycleScope.launchWhenStarted {
...
}
lifecycleScope.launchWhenResumed {
...
}
}
}
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
...
}
lifecycleScope.launchWhenCreated {
...
}
lifecycleScope.launchWhenStarted {
...
}
lifecycleScope.launchWhenResumed {
...
}
}
}
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
...
}
}
}
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
...
}
}
}
ViewModel




A classe ViewModel possui agora a propriedade viewModelScope.
ViewModel Scope
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-beta01"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.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 {
// load from web
}
}
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 {
// load from web
}
}
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val viewModel: MainViewModel by lazy {
ViewModelProviders.of(this).get(MainViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel.message.observe(this, Observer { s ->
txtOutput.text = s
})
viewModel.loadMessage()
}
...
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val viewModel: MainViewModel by lazy {
ViewModelProviders.of(this).get(MainViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel.message.observe(this, Observer { s ->
txtOutput.text = s
})
viewModel.loadMessage()
}
...
WorkManager
WorkManager
dependencies {
def work_version = "2.3.0-alpha02"
implementation "androidx.work:work-runtime-ktx:$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()
}
}
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")
}
})
}
}
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")
}
})
}
}
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")
}
})
}
}
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")
}
})
}
}
• Lifecycle provê um lifecycleScope para Activity e
Fragment (e a view do Fragment).
• ViewModel possui a propriedade viewModelScope.
• WorkManager disponibiliza a classe CoroutineWorker.
Jetpack + Coroutines
Coroutines - Parte 2
• 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.
Iniciando uma coroutine
launch
launch {
txtOutput.text = ""
val time = measureTimeMillis {
val one = withContext(Dispatchers.IO) { loadFirstNumber() }
val two = withContext(Dispatchers.IO) { loadSecondNumber() }
addTextToTextView("The answer is ${one + two}")
}
addTextToTextView("Completed in $time ms")
}
	The	answer	is	42

	Completed	in	2030	ms
async
launch {
txtOutput.text = ""
val time = measureTimeMillis {
val one = async(Dispatchers.IO) { loadFirstNumber() }
val two = async(Dispatchers.IO) { loadSecondNumber() }
val s = one.await() + two.await()
addTextToTextView("The answer is $s")
}
addTextToTextView("Completed in $time ms")
}
async
launch {
txtOutput.text = ""
val time = measureTimeMillis {
val one = async(Dispatchers.IO) { loadFirstNumber() }
val two = async(Dispatchers.IO) { loadSecondNumber() }
val s = one.await() + two.await()
addTextToTextView("The answer is $s")
}
addTextToTextView("Completed in $time ms")
}
async
launch {
txtOutput.text = ""
val time = measureTimeMillis {
val one = async(Dispatchers.IO) { loadFirstNumber() }
val two = async(Dispatchers.IO) { loadSecondNumber() }
val s = one.await() + two.await()
addTextToTextView("The answer is $s")
}
addTextToTextView("Completed in $time ms")
}
async
	The	answer	is	42

	Completed	in	1038	ms
launch {
txtOutput.text = ""
val time = measureTimeMillis {
val one = async(Dispatchers.IO) { loadFirstNumber() }
val two = async(Dispatchers.IO) { loadSecondNumber() }
val s = one.await() + two.await()
addTextToTextView("The answer is $s")
}
addTextToTextView("Completed in $time ms")
}
• O tratamento de exceções é simples, basta tratar no lugar
certo!
• As exceções não tratadas são propagadas para o Job do
escopo.
Exceptions
Exceptions
launch {
txtOutput.text = ""
try {
val result = methodThatThrowsException()
addTextToTextView("Ok $result")
} catch (e: Exception) {
addTextToTextView("Error! ${e.message}")
}
}
&
Exceptions
launch {
txtOutput.text = ""
try {
launch {
val result = methodThatThrowsException()
addTextToTextView("Ok $result")
}
} catch (e: Exception) {
addTextToTextView("Error! ${e.message}")
}
}
Exceptions
launch {
txtOutput.text = ""
try {
launch {
val result = methodThatThrowsException()
addTextToTextView("Ok $result")
}
} catch (e: Exception) {
addTextToTextView("Error! ${e.message}")
}
}
Main 

Thread
Flow
launch

Job
launch
Job
Exceptions
launch {
txtOutput.text = ""
launch {
try {
val result = methodThatThrowsException()
addTextToTextView("Ok $result")
} catch (e: Exception) {
addTextToTextView("Error! ${e.message}")
}
}
}
Exceptions
launch {
txtOutput.text = ""
val task = async {
try {
methodThatThrowsException()
} catch (e: Exception) {
"Error! ${e.message}"
}
}
val result = task.await()
addTextToTextView("Ok $result")
}
Exceptions
launch {
txtOutput.text = ""
val task = async(SupervisorJob(job)) {
methodThatThrowsException()
}
try {
addTextToTextView("Ok ${task.await()}")
} catch (e: Throwable) {
addTextToTextView("Error! ${e.message}")
}
}
Exceptions
launch {
txtOutput.text = ""
try {
coroutineScope {
val task = async {
methodThatThrowsException()
}
addTextToTextView("Ok ${task.await()}")
}
} catch (e: Throwable) {
addTextToTextView("Erro! ${e.message}")
}
}
Exceptions
launch {
txtOutput.text = ""
supervisorScope {
val task = async { methodThatThrowsException() }
try {
addTextToTextView("Ok ${task.await()}")
} catch (e: Throwable) {
addTextToTextView(“Error! ${e.message}")
}
}
}
• 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 se a coroutine foi cancelada, e
isCompleted terminou sua execução.
Cancelamento
• Executa uma coroutine levantando uma
TimeoutCancellationException	caso sua duração
exceda o tempo 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
withTimeout
launch {
txtOutput.text = ""
try {
val s = withTimeout(1300L) {
withContext(Dispatchers.Default) {
aLongOperation()
}
}
txtOutput.text = "Result: $s..."
} catch (e: TimeoutCancellationException) {
txtOutput.text = "Exception! ${e.message}"
}
}
withTimeoutOrNull
launch {
txtOutput.text = ""
val task = async(Dispatchers.Default) {
aLongOperation()
}
val result = withTimeoutOrNull(1300L) { task.await() }
txtOutput.text = "Result: $result"
}
• 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)

}
Convertendo Callbacks
Convertendo Callbacks
object LocationManager {
fun getCurrentLocation(callback: (LatLng?) -> Unit) {
// get the location...
callback(LatLng(-8.187,-36.156))
}
}
LocationManager.getCurrentLocation { latLng ->
if (latLng != null) {
// Exibir localização
} else {
// Tratar o erro
}
}
Convertendo Callbacks
suspend fun getMyLocation(): LatLng {
return suspendCoroutine { continuation ->
LocationManager.getCurrentLocation { latLng ->
if (latLng != null) {
continuation.resume(latLng)
} else {
continuation.resumeWithException(
Exception("Fail to get user location")
)
}
}
}
}
Convertendo Callbacks
suspend fun getMyLocation(): LatLng {
return suspendCoroutine { continuation ->
LocationManager.getCurrentLocation { latLng ->
if (latLng != null) {
continuation.resume(latLng)
} else {
continuation.resumeWithException(
Exception("Fail to get user location")
)
}
}
}
}
Convertendo Callbacks
suspend fun getMyLocation(): LatLng {
return suspendCoroutine { continuation ->
LocationManager.getCurrentLocation { latLng ->
if (latLng != null) {
continuation.resume(latLng)
} else {
continuation.resumeWithException(
Exception("Fail to get user location")
)
}
}
}
}
Convertendo Callbacks
launch{
try {
val latLng = getMyLocation()
// do something
} catch(e: Exception){
// handle error
}
}
• launch (fire-and-forget) e async (para obter um resultado).
• Trate as exceções no launch ou no async. Ou use
SupervisorJob, SupervisorScope ou coroutineScope.
• cancel ou cancelChildren para cancelar o Job ou os jobs
filhos.
• withTimeout ou withTimeoutOrNull.
• Toda suspend function é convertida em um callback usando a
interface Continuation.
Coroutines - Parte 2
“Reactive Coroutines”
Channel
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!")
}
	1	
	4	
	9	
	16	
	25	
	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()
}
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()
}
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()
}
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()
}
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(R.layout.activity_channel) {
private val sender = NumberSender()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getChannel().consumeEach {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
class ChannelActivity : AppCompatActivity(R.layout.activity_channel) {
private val sender = NumberSender()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getChannel().consumeEach {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
class ChannelActivity : AppCompatActivity(R.layout.activity_channel) {
private val sender = NumberSender()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getChannel().consumeEach {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
• Flow é uma abstração de um cold stream.
• Nada é executado/emitido até que algum consumidor se
registre no fluxo.
• Diversos operadores como no RxJava.
Flow
@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)
}
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!")
}
launch {
txtOutput.text = ""
(0..100).asFlow()
.map { it * it }
.filter { it % 4 == 0 } // here and above is on IO thread pool
.flowOn(Dispatchers.IO) // 'change the upstream Dispatcher
.map { it * 2 }
.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()
}
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(R.layout.activity_flow) {
private val sender = NumberFlow()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getFlow().collect {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
class FlowActivity : AppCompatActivity(R.layout.activity_flow) {
private val sender = NumberFlow()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnProduce.setOnClickListener {
lifecycleScope.launch {
sender.sendNext()
}
}
lifecycleScope.launch {
sender.getFlow().collect {
txtOutput.append("Number: $it n")
}
}
}
override fun onDestroy() {
super.onDestroy()
sender.close()
}
}
class FlowActivity : AppCompatActivity(R.layout.activity_flow) {
private val sender = NumberFlow()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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.2.0"
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")
fun getAll(): Flow<List<User>>
@Query("SELECT * FROM user WHERE uid = :id")
fun getUser(id: Long): Flow<User?>
@Insert
suspend fun insert(users: User): Long
@Delete
suspend fun delete(user: User)
}
launch {
dao.getAll().collect { usersList ->
lstNames.adapter = UserAdapter(context,
usersList
)
}
}
launch {
withContext(Dispatchers.IO) {
val id = dao.insert(
User(0,
edtFirstName.text.toString(),
edtLastName.text.toString())
)
}
edtFirstName.text.clear()
edtLastName.text.clear()
edtFirstName.requestFocus()
}
• 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
Coroutines (ex: Retrofit, Apollo, MockK, …).
Conclusão
• 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 #1
• 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 #2
• 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 #3
• 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
Referências #4
Obrigado!
Nelson Glauber
@nglauber

More Related Content

What's hot

Introduction to React
Introduction to ReactIntroduction to React
Introduction to React
Rob Quick
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
Designveloper
 
Completable future
Completable futureCompletable future
Completable future
Srinivasan Raghvan
 
Reactjs
Reactjs Reactjs
Reactjs
Neha Sharma
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js Simplified
Sunil Yadav
 
IoC and Mapper in C#
IoC and Mapper in C#IoC and Mapper in C#
IoC and Mapper in C#
Huy Hoàng Phạm
 
React Django Presentation
React Django PresentationReact Django Presentation
React Django Presentation
Allison DiNapoli
 
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019min woog kim
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
Varun Raj
 
React render props
React render propsReact render props
React render props
Saikat Samanta
 
React hooks
React hooksReact hooks
React hooks
Assaf Gannon
 
React native
React nativeReact native
ReactJS presentation
ReactJS presentationReactJS presentation
ReactJS presentation
Thanh Tuong
 
A Brief Introduction to React.js
A Brief Introduction to React.jsA Brief Introduction to React.js
A Brief Introduction to React.js
Doug Neiner
 
Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018
Roman Elizarov
 
Introduction to react_js
Introduction to react_jsIntroduction to react_js
Introduction to react_js
MicroPyramid .
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Edureka!
 
AEM Meetup Personalization with ContextHub
AEM Meetup Personalization with ContextHubAEM Meetup Personalization with ContextHub
AEM Meetup Personalization with ContextHub
Abhishek Dwevedi
 
Svelte the future of frontend development
Svelte   the future of frontend developmentSvelte   the future of frontend development
Svelte the future of frontend development
twilson63
 
React lecture
React lectureReact lecture
React lecture
Christoffer Noring
 

What's hot (20)

Introduction to React
Introduction to ReactIntroduction to React
Introduction to React
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
 
Completable future
Completable futureCompletable future
Completable future
 
Reactjs
Reactjs Reactjs
Reactjs
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js Simplified
 
IoC and Mapper in C#
IoC and Mapper in C#IoC and Mapper in C#
IoC and Mapper in C#
 
React Django Presentation
React Django PresentationReact Django Presentation
React Django Presentation
 
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
 
React render props
React render propsReact render props
React render props
 
React hooks
React hooksReact hooks
React hooks
 
React native
React nativeReact native
React native
 
ReactJS presentation
ReactJS presentationReactJS presentation
ReactJS presentation
 
A Brief Introduction to React.js
A Brief Introduction to React.jsA Brief Introduction to React.js
A Brief Introduction to React.js
 
Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018
 
Introduction to react_js
Introduction to react_jsIntroduction to react_js
Introduction to react_js
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
 
AEM Meetup Personalization with ContextHub
AEM Meetup Personalization with ContextHubAEM Meetup Personalization with ContextHub
AEM Meetup Personalization with ContextHub
 
Svelte the future of frontend development
Svelte   the future of frontend developmentSvelte   the future of frontend development
Svelte the future of frontend development
 
React lecture
React lectureReact lecture
React lecture
 

Similar to Aplicações Assíncronas no Android com Coroutines e Jetpack

Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
Nelson Glauber Leal
 
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
Nelson Glauber Leal
 
Aplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackAplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & Jetpack
Nelson Glauber Leal
 
Kotlin from-scratch 3 - coroutines
Kotlin from-scratch 3 - coroutinesKotlin from-scratch 3 - coroutines
Kotlin from-scratch 3 - coroutines
Franco Lombardo
 
Improving app performance with Kotlin Coroutines
Improving app performance with Kotlin CoroutinesImproving app performance with Kotlin Coroutines
Improving app performance with Kotlin Coroutines
Hassan Abid
 
droidcon Transylvania - Kotlin Coroutines
droidcon Transylvania - Kotlin Coroutinesdroidcon Transylvania - Kotlin Coroutines
droidcon Transylvania - Kotlin Coroutines
Arthur Nagy
 
Docker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline ExecutionDocker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline Execution
Brennan Saeta
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
MongoDB
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
KatyShimizu
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
KatyShimizu
 
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
 
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!
Stacy Devino
 
The Ring programming language version 1.7 book - Part 75 of 196
The Ring programming language version 1.7 book - Part 75 of 196The Ring programming language version 1.7 book - Part 75 of 196
The Ring programming language version 1.7 book - Part 75 of 196
Mahmoud Samir Fayed
 
Get you Java application ready for Kubernetes !
Get you Java application ready for Kubernetes !Get you Java application ready for Kubernetes !
Get you Java application ready for Kubernetes !
Anthony Dahanne
 
The Ring programming language version 1.8 book - Part 77 of 202
The Ring programming language version 1.8 book - Part 77 of 202The Ring programming language version 1.8 book - Part 77 of 202
The Ring programming language version 1.8 book - Part 77 of 202
Mahmoud Samir Fayed
 
An intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECSAn intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECS
Yevgeniy Brikman
 
Binary Studio Academy: Concurrency in C# 5.0
Binary Studio Academy: Concurrency in C# 5.0Binary Studio Academy: Concurrency in C# 5.0
Binary Studio Academy: Concurrency in C# 5.0
Binary Studio
 
.NET Multithreading/Multitasking
.NET Multithreading/Multitasking.NET Multithreading/Multitasking
.NET Multithreading/Multitasking
Sasha Kravchuk
 
OSGi Community Event 2010 - Dependencies, dependencies, dependencies
OSGi Community Event 2010 - Dependencies, dependencies, dependenciesOSGi Community Event 2010 - Dependencies, dependencies, dependencies
OSGi Community Event 2010 - Dependencies, dependencies, dependencies
mfrancis
 
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
Alex Payne
 

Similar to Aplicações Assíncronas no Android com Coroutines e Jetpack (20)

Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
 
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
 
Aplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackAplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & Jetpack
 
Kotlin from-scratch 3 - coroutines
Kotlin from-scratch 3 - coroutinesKotlin from-scratch 3 - coroutines
Kotlin from-scratch 3 - coroutines
 
Improving app performance with Kotlin Coroutines
Improving app performance with Kotlin CoroutinesImproving app performance with Kotlin Coroutines
Improving app performance with Kotlin Coroutines
 
droidcon Transylvania - Kotlin Coroutines
droidcon Transylvania - Kotlin Coroutinesdroidcon Transylvania - Kotlin Coroutines
droidcon Transylvania - Kotlin Coroutines
 
Docker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline ExecutionDocker & ECS: Secure Nearline Execution
Docker & ECS: Secure Nearline Execution
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
 
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
 
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!
 
The Ring programming language version 1.7 book - Part 75 of 196
The Ring programming language version 1.7 book - Part 75 of 196The Ring programming language version 1.7 book - Part 75 of 196
The Ring programming language version 1.7 book - Part 75 of 196
 
Get you Java application ready for Kubernetes !
Get you Java application ready for Kubernetes !Get you Java application ready for Kubernetes !
Get you Java application ready for Kubernetes !
 
The Ring programming language version 1.8 book - Part 77 of 202
The Ring programming language version 1.8 book - Part 77 of 202The Ring programming language version 1.8 book - Part 77 of 202
The Ring programming language version 1.8 book - Part 77 of 202
 
An intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECSAn intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECS
 
Binary Studio Academy: Concurrency in C# 5.0
Binary Studio Academy: Concurrency in C# 5.0Binary Studio Academy: Concurrency in C# 5.0
Binary Studio Academy: Concurrency in C# 5.0
 
.NET Multithreading/Multitasking
.NET Multithreading/Multitasking.NET Multithreading/Multitasking
.NET Multithreading/Multitasking
 
OSGi Community Event 2010 - Dependencies, dependencies, dependencies
OSGi Community Event 2010 - Dependencies, dependencies, dependenciesOSGi Community Event 2010 - Dependencies, dependencies, dependencies
OSGi Community Event 2010 - Dependencies, dependencies, dependencies
 
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
 

More from Nelson Glauber Leal

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
Nelson Glauber Leal
 
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
Nelson Glauber Leal
 
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
Nelson Glauber Leal
 
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)
Nelson Glauber Leal
 
Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021
Nelson Glauber Leal
 
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
Nelson Glauber Leal
 
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
Nelson Glauber Leal
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
Nelson Glauber Leal
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
Nelson Glauber Leal
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard Library
Nelson Glauber Leal
 
Introdução ao Desenvolvimento Android com Kotlin
Introdução ao Desenvolvimento Android com KotlinIntrodução ao Desenvolvimento Android com Kotlin
Introdução ao Desenvolvimento Android com Kotlin
Nelson Glauber Leal
 
Persisting Data on SQLite using Room
Persisting Data on SQLite using RoomPersisting Data on SQLite using Room
Persisting Data on SQLite using Room
Nelson Glauber Leal
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
Nelson Glauber Leal
 
Desenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de Aplicativos AndroidDesenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de Aplicativos Android
Nelson Glauber Leal
 
Desenvolvimento Moderno de aplicativos Android
Desenvolvimento Moderno de aplicativos AndroidDesenvolvimento Moderno de aplicativos Android
Desenvolvimento Moderno de aplicativos Android
Nelson Glauber Leal
 
Turbinando o desenvolvimento Android com Kotlin
Turbinando o desenvolvimento Android com KotlinTurbinando o desenvolvimento Android com Kotlin
Turbinando o desenvolvimento Android com Kotlin
Nelson Glauber Leal
 
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
Nelson Glauber Leal
 
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
Nelson Glauber Leal
 
The world of Android Animations
The world of Android AnimationsThe world of Android Animations
The world of Android Animations
Nelson Glauber Leal
 
Android Constraint Layout
Android Constraint LayoutAndroid Constraint Layout
Android Constraint Layout
Nelson Glauber Leal
 

More from 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
 
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
 
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
 
Introdução ao Desenvolvimento Android com Kotlin
Introdução ao Desenvolvimento Android com KotlinIntrodução ao Desenvolvimento Android com Kotlin
Introdução ao Desenvolvimento Android com Kotlin
 
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
 

Recently uploaded

How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?
ToXSL Technologies
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
Luigi Fugaro
 
Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...
Paul Brebner
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
Patrick Weigel
 
Building API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructureBuilding API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructure
confluent
 
What is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdfWhat is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdf
kalichargn70th171
 
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Paul Brebner
 
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdfBaha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid
 
Upturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in NashikUpturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in Nashik
Upturn India Technologies
 
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom KittEnhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Peter Caitens
 
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
dakas1
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
Drona Infotech
 
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
kalichargn70th171
 
Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...
Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...
Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...
Luigi Fugaro
 
ppt on the brain chip neuralink.pptx
ppt  on   the brain  chip neuralink.pptxppt  on   the brain  chip neuralink.pptx
ppt on the brain chip neuralink.pptx
Reetu63
 
Boost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management AppsBoost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management Apps
Jhone kinadey
 
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data PlatformAlluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio, Inc.
 
Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...
OnePlan Solutions
 

Recently uploaded (20)

How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
 
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
 
Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
 
Building API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructureBuilding API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructure
 
What is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdfWhat is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdf
 
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
 
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdfBaha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
 
Upturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in NashikUpturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in Nashik
 
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom KittEnhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
 
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
 
bgiolcb
bgiolcbbgiolcb
bgiolcb
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
 
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
 
Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...
Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...
Voxxed Days Trieste 2024 - Unleashing the Power of Vector Search and Semantic...
 
ppt on the brain chip neuralink.pptx
ppt  on   the brain  chip neuralink.pptxppt  on   the brain  chip neuralink.pptx
ppt on the brain chip neuralink.pptx
 
Boost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management AppsBoost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management Apps
 
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data PlatformAlluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
 
Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...
 

Aplicações Assíncronas no Android com Coroutines e Jetpack

  • 1. Aplicações assíncronas no Android com
 Coroutines & Jetpack Nelson Glauber @nglauber
  • 2. • Carregar arquivo de layout • Desenhar as views • Tratar os eventos de UI • … A main thread do Android
  • 3. • O processamento desses eventos deve ocorrer em: • Menos de 16ms para devices com taxas de atualização de 60Hz • Menos de 12ms para dispositivos com taxas de 90Hz • Menos de < 8ms para dispositivos com taxas de 120Hz A main thread do Android
  • 4. A main thread do Android
  • 5. A main thread do Android
  • 7. • AsyncTask ! • Thread + Handler " • Loaders (deprecated) ! • Volley #$ • RxJava 🧐 Async no Android
  • 9. • Essencialmente, coroutines são light-weight threads. • Fácil de usar (sem mais “callbacks hell” e/ou centenas de operadores). • Úteis para qualquer tarefa computacional mais onerosa (como operações de I/O). • Permite a substituição de callbacks por operações assíncronas. Coroutines
  • 11. • Suspending functions são o centro de tudo em Coroutines. • São 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. suspend
  • 12. import kotlinx.coroutines.delay class Calculator { suspend fun sum(a: Int, b: Int): Int { delay(5_000) return a + b } }
  • 13. import kotlinx.coroutines.delay class Calculator { suspend fun sum(a: Int, b: Int): Int { delay(5_000) return a + b } } import kotlinx.coroutines.runBlocking import org.junit.* class CalculatorUnitTest { @Test fun sum_isCorrect() = runBlocking { val calc = Calculator() assertEquals(4, calc.sum(2, 2)) } }
  • 14. • Job • Context • Scope • Dispatcher Coroutines no Android
  • 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() } // update the UI using books } } ...
  • 16. 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() } // update the UI using books } } ... Job • Um Job representa uma tarefa ou conjunto de tarefas em execução. • Pode possuir “filhos”. • A função launch retorna um Job. • Pode ser cancelado usando a função cancel.
  • 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() } // update the UI using books } } ... Context • A interface CoroutineContext Representa o conjunto de atributos que configuram uma coroutine. • Pode definir a política de threading, tratamento de exceções, etc.
  • 18. 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() } // update the UI using books } } ... Scope • Coroutines sempre rodam em um escopo. • Serve como uma espécie de ciclo de vida para um conjunto de coroutines. • Permite um maior controle das tarefas em execução.
  • 19. 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() } // update the UI using books } } ... Dispatcher • Define o pool de threads 
 onde a coroutine executará. • Default: para processos 
 que usam a CPU mais intensamente. • IO: para tarefas de rede 
 ou arquivos. O pool de threads é compartilhado com o dispatcher DEFAULT. • Main - main thread do Android.
  • 20. 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() } // update the UI using books } } ...
  • 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() } // update the UI using books } } ...
  • 22. fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } // update the UI using books } }
  • 23. fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = BookHttp.loadBooks() // update the UI using books } } 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)
  • 24. fun callWebService() { coroutineScope.launch(Dispatchers.IO){ txtOutput.text = "" val books = BookHttp.loadBooks() // update the UI using books } } 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)
  • 25. fun callWebService() { coroutineScope.launch { txtOutput.text = "" val books = withContext(Dispatchers.IO) { BookHttp.loadBooks() } // update the UI using books } } &
  • 26. • Suspending functions • Job • Context • Scope • Dispatcher Coroutines
  • 28. • 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. Lifecycle Scope dependencies { ... implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-beta01" }
  • 29. class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { ... } lifecycleScope.launchWhenCreated { ... } lifecycleScope.launchWhenStarted { ... } lifecycleScope.launchWhenResumed { ... } } }
  • 30. class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { ... } lifecycleScope.launchWhenCreated { ... } lifecycleScope.launchWhenStarted { ... } lifecycleScope.launchWhenResumed { ... } } }
  • 31. class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { ... } lifecycleScope.launchWhenCreated { ... } lifecycleScope.launchWhenStarted { ... } lifecycleScope.launchWhenResumed { ... } } }
  • 32. class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { ... } lifecycleScope.launchWhenCreated { ... } lifecycleScope.launchWhenStarted { ... } lifecycleScope.launchWhenResumed { ... } } }
  • 33. class MyFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewLifecycleOwner.lifecycleScope.launch { ... } } }
  • 34. class MyFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewLifecycleOwner.lifecycleScope.launch { ... } } }
  • 36. 
 
 A classe ViewModel possui agora a propriedade viewModelScope. ViewModel Scope dependencies { implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-beta01" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-beta01" }
  • 37. 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 { // load from web } }
  • 38. 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 { // load from web } }
  • 39. class MainActivity : AppCompatActivity(R.layout.activity_main) { private val viewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { ... viewModel.message.observe(this, Observer { s -> txtOutput.text = s }) viewModel.loadMessage() } ...
  • 40. class MainActivity : AppCompatActivity(R.layout.activity_main) { private val viewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { ... viewModel.message.observe(this, Observer { s -> txtOutput.text = s }) viewModel.loadMessage() } ...
  • 42. WorkManager dependencies { def work_version = "2.3.0-alpha02" implementation "androidx.work:work-runtime-ktx:$work_version" }
  • 43. 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() } }
  • 44. 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() } }
  • 45. 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") } }) } }
  • 46. 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") } }) } }
  • 47. 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") } }) } }
  • 48. 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") } }) } }
  • 49. • Lifecycle provê um lifecycleScope para Activity e Fragment (e a view do Fragment). • ViewModel possui a propriedade viewModelScope. • WorkManager disponibiliza a classe CoroutineWorker. Jetpack + Coroutines
  • 51. • 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. Iniciando uma coroutine
  • 52. launch launch { txtOutput.text = "" val time = measureTimeMillis { val one = withContext(Dispatchers.IO) { loadFirstNumber() } val two = withContext(Dispatchers.IO) { loadSecondNumber() } addTextToTextView("The answer is ${one + two}") } addTextToTextView("Completed in $time ms") } The answer is 42
 Completed in 2030 ms
  • 53. async launch { txtOutput.text = "" val time = measureTimeMillis { val one = async(Dispatchers.IO) { loadFirstNumber() } val two = async(Dispatchers.IO) { loadSecondNumber() } val s = one.await() + two.await() addTextToTextView("The answer is $s") } addTextToTextView("Completed in $time ms") }
  • 54. async launch { txtOutput.text = "" val time = measureTimeMillis { val one = async(Dispatchers.IO) { loadFirstNumber() } val two = async(Dispatchers.IO) { loadSecondNumber() } val s = one.await() + two.await() addTextToTextView("The answer is $s") } addTextToTextView("Completed in $time ms") }
  • 55. async launch { txtOutput.text = "" val time = measureTimeMillis { val one = async(Dispatchers.IO) { loadFirstNumber() } val two = async(Dispatchers.IO) { loadSecondNumber() } val s = one.await() + two.await() addTextToTextView("The answer is $s") } addTextToTextView("Completed in $time ms") }
  • 56. async The answer is 42
 Completed in 1038 ms launch { txtOutput.text = "" val time = measureTimeMillis { val one = async(Dispatchers.IO) { loadFirstNumber() } val two = async(Dispatchers.IO) { loadSecondNumber() } val s = one.await() + two.await() addTextToTextView("The answer is $s") } addTextToTextView("Completed in $time ms") }
  • 57. • O tratamento de exceções é simples, basta tratar no lugar certo! • As exceções não tratadas são propagadas para o Job do escopo. Exceptions
  • 58. Exceptions launch { txtOutput.text = "" try { val result = methodThatThrowsException() addTextToTextView("Ok $result") } catch (e: Exception) { addTextToTextView("Error! ${e.message}") } } &
  • 59. Exceptions launch { txtOutput.text = "" try { launch { val result = methodThatThrowsException() addTextToTextView("Ok $result") } } catch (e: Exception) { addTextToTextView("Error! ${e.message}") } }
  • 60. Exceptions launch { txtOutput.text = "" try { launch { val result = methodThatThrowsException() addTextToTextView("Ok $result") } } catch (e: Exception) { addTextToTextView("Error! ${e.message}") } } Main 
 Thread Flow launch
 Job launch Job
  • 61. Exceptions launch { txtOutput.text = "" launch { try { val result = methodThatThrowsException() addTextToTextView("Ok $result") } catch (e: Exception) { addTextToTextView("Error! ${e.message}") } } }
  • 62. Exceptions launch { txtOutput.text = "" val task = async { try { methodThatThrowsException() } catch (e: Exception) { "Error! ${e.message}" } } val result = task.await() addTextToTextView("Ok $result") }
  • 63. Exceptions launch { txtOutput.text = "" val task = async(SupervisorJob(job)) { methodThatThrowsException() } try { addTextToTextView("Ok ${task.await()}") } catch (e: Throwable) { addTextToTextView("Error! ${e.message}") } }
  • 64. Exceptions launch { txtOutput.text = "" try { coroutineScope { val task = async { methodThatThrowsException() } addTextToTextView("Ok ${task.await()}") } } catch (e: Throwable) { addTextToTextView("Erro! ${e.message}") } }
  • 65. Exceptions launch { txtOutput.text = "" supervisorScope { val task = async { methodThatThrowsException() } try { addTextToTextView("Ok ${task.await()}") } catch (e: Throwable) { addTextToTextView(“Error! ${e.message}") } } }
  • 66. • 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 se a coroutine foi cancelada, e isCompleted terminou sua execução. Cancelamento
  • 67. • Executa uma coroutine levantando uma TimeoutCancellationException caso sua duração exceda o tempo 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
  • 68. withTimeout launch { txtOutput.text = "" try { val s = withTimeout(1300L) { withContext(Dispatchers.Default) { aLongOperation() } } txtOutput.text = "Result: $s..." } catch (e: TimeoutCancellationException) { txtOutput.text = "Exception! ${e.message}" } }
  • 69. withTimeoutOrNull launch { txtOutput.text = "" val task = async(Dispatchers.Default) { aLongOperation() } val result = withTimeoutOrNull(1300L) { task.await() } txtOutput.text = "Result: $result" }
  • 70. • 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)
 } Convertendo Callbacks
  • 71. Convertendo Callbacks object LocationManager { fun getCurrentLocation(callback: (LatLng?) -> Unit) { // get the location... callback(LatLng(-8.187,-36.156)) } } LocationManager.getCurrentLocation { latLng -> if (latLng != null) { // Exibir localização } else { // Tratar o erro } }
  • 72. Convertendo Callbacks suspend fun getMyLocation(): LatLng { return suspendCoroutine { continuation -> LocationManager.getCurrentLocation { latLng -> if (latLng != null) { continuation.resume(latLng) } else { continuation.resumeWithException( Exception("Fail to get user location") ) } } } }
  • 73. Convertendo Callbacks suspend fun getMyLocation(): LatLng { return suspendCoroutine { continuation -> LocationManager.getCurrentLocation { latLng -> if (latLng != null) { continuation.resume(latLng) } else { continuation.resumeWithException( Exception("Fail to get user location") ) } } } }
  • 74. Convertendo Callbacks suspend fun getMyLocation(): LatLng { return suspendCoroutine { continuation -> LocationManager.getCurrentLocation { latLng -> if (latLng != null) { continuation.resume(latLng) } else { continuation.resumeWithException( Exception("Fail to get user location") ) } } } }
  • 75. Convertendo Callbacks launch{ try { val latLng = getMyLocation() // do something } catch(e: Exception){ // handle error } }
  • 76. • launch (fire-and-forget) e async (para obter um resultado). • Trate as exceções no launch ou no async. Ou use SupervisorJob, SupervisorScope ou coroutineScope. • cancel ou cancelChildren para cancelar o Job ou os jobs filhos. • withTimeout ou withTimeoutOrNull. • Toda suspend function é convertida em um callback usando a interface Continuation. Coroutines - Parte 2
  • 79. 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!") } 1 4 9 16 25 Done!
  • 80. 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() }
  • 81. 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() }
  • 82. 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() }
  • 83. 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() }
  • 84. 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() }
  • 85. class ChannelActivity : AppCompatActivity(R.layout.activity_channel) { private val sender = NumberSender() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getChannel().consumeEach { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 86. class ChannelActivity : AppCompatActivity(R.layout.activity_channel) { private val sender = NumberSender() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getChannel().consumeEach { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 87. class ChannelActivity : AppCompatActivity(R.layout.activity_channel) { private val sender = NumberSender() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getChannel().consumeEach { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 88. • Flow é uma abstração de um cold stream. • Nada é executado/emitido até que algum consumidor se registre no fluxo. • Diversos operadores como no RxJava. Flow
  • 89. @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) }
  • 90. 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!") }
  • 91. launch { txtOutput.text = "" (0..100).asFlow() .map { it * it } .filter { it % 4 == 0 } // here and above is on IO thread pool .flowOn(Dispatchers.IO) // 'change the upstream Dispatcher .map { it * 2 } .flowOn(Dispatchers.Main) .collect {number -> addTextToTextView("$numbern") } }
  • 92. 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() }
  • 93. 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() }
  • 94. class FlowActivity : AppCompatActivity(R.layout.activity_flow) { private val sender = NumberFlow() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getFlow().collect { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 95. class FlowActivity : AppCompatActivity(R.layout.activity_flow) { private val sender = NumberFlow() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getFlow().collect { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 96. class FlowActivity : AppCompatActivity(R.layout.activity_flow) { private val sender = NumberFlow() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btnProduce.setOnClickListener { lifecycleScope.launch { sender.sendNext() } } lifecycleScope.launch { sender.getFlow().collect { txtOutput.append("Number: $it n") } } } override fun onDestroy() { super.onDestroy() sender.close() } }
  • 97. Room
  • 98. Room dependencies { def room_version = "2.2.0" 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" ... }
  • 99. @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) }
  • 100. @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): Flow<List<User>> @Query("SELECT * FROM user WHERE uid = :id") fun getUser(id: Long): Flow<User?> @Insert suspend fun insert(users: User): Long @Delete suspend fun delete(user: User) }
  • 101. launch { dao.getAll().collect { usersList -> lstNames.adapter = UserAdapter(context, usersList ) } }
  • 102. launch { withContext(Dispatchers.IO) { val id = dao.insert( User(0, edtFirstName.text.toString(), edtLastName.text.toString()) ) } edtFirstName.text.clear() edtLastName.text.clear() edtFirstName.requestFocus() }
  • 103. • 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 Coroutines (ex: Retrofit, Apollo, MockK, …). Conclusão
  • 104. • 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 #1
  • 105. • 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 #2
  • 106. • 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 #3
  • 107. • 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 Referências #4