SlideShare une entreprise Scribd logo
1  sur  105
Télécharger pour lire hors ligne
Managing parallelism
using coroutines
Fabio Collini
@fabioCollini
MainDispatcher
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

val textView = findViewById<TextView>(R.id.text)

textView.text = "1"

Thread.sleep(2000)

textView.text = "2"

Thread.sleep(2000)

textView.text = "3"

}1

}2
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

val textView = findViewById<TextView>(R.id.text)

textView.text = "1"

Thread.sleep(2000)

textView.text = "2"

Thread.sleep(2000)

textView.text = "3"

}1

}2
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

val textView = findViewById<TextView>(R.id.text)

lifecycleScope.launch {

textView.text = "1"

delay(2000)

textView.text = "2"

delay(2000)

textView.text = "3"

}3

}1

}2
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

val textView = findViewById<TextView>(R.id.text)

lifecycleScope.launch {

textView.text = "1"

delay(2000)

textView.text = "2"

delay(2000)

textView.text = "3"

}3

}1

}2
internal class AndroidDispatcherFactory : MainDispatcherFactory {

override fun createDispatcher(allFactories: List<MainDispatcherFactory>) =

HandlerContext(Looper.getMainLooper().asHandler(async = true), "Main")

!//!!...

}1
kotlinx-coroutines-android
internal class AndroidDispatcherFactory : MainDispatcherFactory {

override fun createDispatcher(allFactories: List<MainDispatcherFactory>) =

HandlerContext(Looper.getMainLooper().asHandler(async = true), "Main")

!//!!...

}1
kotlinx-coroutines-android
internal class HandlerContext private constructor(

private val handler: Handler,

private val name: String?,

private val invokeImmediately: Boolean

) : HandlerDispatcher(), Delay {

!//!!...

override fun isDispatchNeeded(context: CoroutineContext): Boolean {

return !invokeImmediately "|| Looper.myLooper() "!= handler.looper

}2

override fun dispatch(context: CoroutineContext, block: Runnable) {

handler.post(block)

}3

!//!!...

override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {

handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))

return object : DisposableHandle {

override fun dispose() {

handler.removeCallbacks(block)

}4

}5

}6

}7
kotlinx-coroutines-android
internal class HandlerContext private constructor(

private val handler: Handler,

private val name: String?,

private val invokeImmediately: Boolean

) : HandlerDispatcher(), Delay {

!//!!...

override fun isDispatchNeeded(context: CoroutineContext): Boolean {

return !invokeImmediately "|| Looper.myLooper() "!= handler.looper

}2

override fun dispatch(context: CoroutineContext, block: Runnable) {

handler.post(block)

}3

!//!!...

override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {

handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))

return object : DisposableHandle {

override fun dispose() {

handler.removeCallbacks(block)

}4

}5

}6

}7
kotlinx-coroutines-android
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

val textView = findViewById<TextView>(R.id.text)

lifecycleScope.launch {

textView.text = "1"

delay(2000)

textView.text = "2"

delay(2000)

textView.text = "3"

}6

}

}
lifecycleScope.launch {

textView.text = "1"

delay(2000)

textView.text = "2"

delay(2000)

textView.text = "3"

}6
lifecycleScope.launch {

textView.text = "1"

delay(2000)

textView.text = "2"

delay(2000)

textView.text = "3"

}6
val handler = Handler()

textView.text = "1"

handler.postDelayed({

textView.text = "2"

handler.postDelayed({

textView.text = "3"

}, 2000)

}, 2000)
Main safe
interface StackOverflowService {

@GET("/users")

suspend fun fetchTopUsers(): List<User>

@GET("/users/{userId}/badges")

suspend fun fetchBadges(

@Path("userId") userId: Int

): List<Badge>

@GET("/users/{userId}/top-tags")

suspend fun fetchTags(

@Path("userId") userId: Int

): List<Tag>

}
class Storage {

fun load(): List<User>? {3

val content = FileInputStream(getCacheFile())

.bufferedReader()

.use { it.readText() }

return parseData(content)

}1

}2
class Storage {

fun load(): List<User>? = withContext(Dispatchers.IO) {3

val content = FileInputStream(getCacheFile())

.bufferedReader()

.use { it.readText() }

return parseData(content)

}1

}2
class Storage {

suspend fun load(): List<User>? = withContext(Dispatchers.IO) {

val content = FileInputStream(getCacheFile())

.bufferedReader()

.use { it.readText() }

return parseData(content)

}1

}2
viewModelScope.launch {

!//!!...

val dataFromCache = storage.load()

val data = if (dataFromCache "!= null) {

dataFromCache

} else {

val fetchedData = api.fetchTopUsers()[0]

storage.save(fetchedData)

fetchedData

}3

updateUi(data)

!//!!...

}1
viewModelScope.launch {

!//!!...

val dataFromCache = storage.load()

val data = if (dataFromCache "!= null) {

dataFromCache

} else {

val fetchedData = api.fetchTopUsers()[0]

println("where am I executed?")

storage.save(fetchedData)

fetchedData

}3

updateUi(data)

!//!!...

}1
main

main

io

main

main

main

io

main

io

main

main

main

main

main
viewModelScope.launch {

!//!!...

val data = withContext(Dispatchers.IO) {

val dataFromCache = storage.load()

if (dataFromCache "!= null) {

dataFromCache

} else {

val fetchedData = api.fetchTopUsers()[0]

println("where am I executed?")

storage.save(fetchedData)

fetchedData

}3

}

updateUi(data)

!//!!...

}1
main

main 

io

io

io

io

io

io

io

io 

io 

io 

io

main

main

main
api.fetchTopUsers()[0]
suspend fun topUser(): User {

return api.fetchTopUsers()[0]

}1
suspend fun topUser(): UserStats {

val user = api.fetchTopUsers()[0]

val badges = api.fetchBadges(user.id)

val tags = api.fetchTags(user.id)

return UserStats(

user,

badges,

tags

)2

}1
suspend fun topUser(): UserStats {

val user = api.fetchTopUsers()[0]

val badges = api.fetchBadges(user.id)

val tags = api.fetchTags(user.id)

return UserStats(

user,

badges,

tags

)2

}1
fun topUser(): Single<UserStats> =

api.fetchTopUsers()

.map { users "-> users[0] }

.flatMap { user "->

api.fetchBadges(user.id)

.flatMap { badges "->

api.fetchTags(user.id).map { tags "->

UserStats(user, badges, tags)

}

}

}
RxJavaCoroutines
Async
suspend fun topUser(): UserStats {

val user = api.fetchTopUsers()[0]

val badges = api.fetchBadges(user.id)

val tags = api.fetchTags(user.id)

return UserStats(

user,

badges,

tags

)2

}1
suspend fun topUser(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}

return UserStats(

user,

badges,

tags

)2

}1
suspend fun topUser(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}

return UserStats(

user,

badges,

tags

)2

}1
viewModelScope.launch {6

val duration = measureTimeMillis {

coroutineScope { 

val v1 = async {7

Thread.sleep(1000)

1

}1

val v2 = async {8

Thread.sleep(1000)

2

}2



v1.await() + v2.await()

}3

}4

println("Duration: $duration")

}5
Duration: 2000
MainDispatcher
Mainthread
sleepsleep
viewModelScope.launch(Dispatchers.IO) {6

val duration = measureTimeMillis {

coroutineScope { 

val v1 = async {7

Thread.sleep(1000)

1

}1

val v2 = async {8

Thread.sleep(1000)

2

}2



v1.await() + v2.await()

}3

}4

println("Duration: $duration")

}5
Duration: 1000
Dispatchers.IO
IOthread1
sleep
IOthread2
sleep
IOthreadn
viewModelScope.launch {6

val duration = measureTimeMillis {

coroutineScope { 

val v1 = async(Dispatchers.IO) {7

Thread.sleep(1000)

1

}1

val v2 = async(Dispatchers.IO) {8

Thread.sleep(1000)

2

}2



v1.await() + v2.await()

}3

}4

println("Duration: $duration")

}5
Duration: 1000
Dispatchers.IO
IOthread1
sleep
IOthread2
sleep
IOthreadn
viewModelScope.launch {6

val duration = measureTimeMillis {

coroutineScope { 

val v1 = async {7

delay(1000)

1

}1

val v2 = async {8

delay(1000)

2

}2



v1.await() + v2.await()

}3

}4

println("Duration: $duration")

}5
Duration: 1000
MainDispatcher
Mainthread
delay
delay
!!/**

* !!...

* The resulting coroutine has a key difference compared with similar 

* primitives in other languages and frameworks: 

* it cancels the parent job (or outer scope) on failure

* to enforce structured concurrency paradigm.

* !!...

!*/

fun <T> CoroutineScope.async(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> T

): Deferred<T>
!!/**

* !!...

* The resulting coroutine has a key difference compared with similar 

* primitives in other languages and frameworks: 

* it cancels the parent job (or outer scope) on failure

* to enforce structured concurrency paradigm.

* !!...

!*/

fun <T> CoroutineScope.async(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> T

): Deferred<T>
!!/**

* !!...

* The resulting coroutine has a key difference compared with similar 

* primitives in other languages and frameworks: 

* it cancels the parent job (or outer scope) on failure

* to enforce structured concurrency paradigm.

* !!...

!*/

fun <T> CoroutineScope.async(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> T

): Deferred<T>
viewModelScope.launch {

try {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}

updateUi(UserStats(user, badges, tags))

} catch (e: Exception) {

showErrorMessage()

}1

}2
viewModelScope
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope.launch {

try {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}

updateUi(UserStats(user, badges, tags))

} catch (e: Exception) {

showErrorMessage()

}1

}2
viewModelScope.launch {

try {

val user = api.fetchTopUsers()[0]

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

updateUi(UserStats(user, badgesDeferred.await(), tagsDeferred.await()))

} catch (e: Exception) {

showErrorMessage()

}1

}2
viewModelScope
fetchTopUsers
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope
fetchTopUsers
async

fetchBadges
async

fetchTags
try
showErrorMessagecatch
updateUi
viewModelScope.launch {

try {

val user = api.fetchTopUsers()[0]

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

updateUi(UserStats(user, badgesDeferred.await(), tagsDeferred.await()))

} catch (e: Exception) {

showErrorMessage()

}1

}2
viewModelScope.launch {

val user = api.fetchTopUsers()[0]

val badgesDeferred = async {

runCatching {

api.fetchBadges(user.id)

}

}

val tagsDeferred = async {

runCatching {

api.fetchTags(user.id)

}

}

val badges = badgesDeferred.await()

val tags = tagsDeferred.await()

if (badges.isFailure "|| tags.isFailure) {

showErrorMessage()

}1

}2
In case of an error
the other is not cancelled
class MyClass(private val api: StackOverflowService) : CoroutineScope {



override val coroutineContext: CoroutineContext

get() = SupervisorJob() + Dispatchers.Main

suspend fun loadData(): UserStats {

val user = api.fetchTopUsers()[0]

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

val badges = badgesDeferred.await()

val tags = tagsDeferred.await()

return UserStats(user, badges, tags)

}1

}2
class MyClass(private val api: StackOverflowService) {

private val scope = MainScope()

suspend fun loadData(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}3

return UserStats(user, badges, tags)

}1

}2
suspend fun loadData(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}3

return UserStats(user, badges, tags)

}1
suspend fun loadData(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

try {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

} catch (e: Exception) {

emptyList<Badge>() to emptyList<Tag>()

}4

}3

return UserStats(user, badges, tags)

}1
loadData
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
emptyListcatch
loadData
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
emptyListcatch
loadData
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
emptyListcatch
loadData
fetchTopUsers
coroutineScope
async

fetchBadges
async

fetchTags
try
emptyListcatch
suspend fun loadData(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = coroutineScope {

try {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

} catch (e: Exception) {

emptyList<Badge>() to emptyList<Tag>()

}4

}3

return UserStats(user, badges, tags)

}1
suspend fun loadData(): UserStats {

val user = api.fetchTopUsers()[0]

val (badges, tags) = try {

coroutineScope {

val badgesDeferred = async { api.fetchBadges(user.id) }

val tagsDeferred = async { api.fetchTags(user.id) }

badgesDeferred.await() to tagsDeferred.await()

}3

} catch (e: Exception) {

emptyList<Badge>() to emptyList<Tag>()

}4

return UserStats(user, badges, tags)

}1
async
Vs
launch
!!/**

* Launches a new coroutine without blocking the current thread and

* returns a reference to the coroutine as a Job.

**/

fun CoroutineScope.launch(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> Unit

): Job

!!/**

* Creates a coroutine and returns its future result as

* an implementation of Deferred.

!*/

fun <T> CoroutineScope.async(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> T

): Deferred<T>
!!/**

* Launches a new coroutine without blocking the current thread and

* returns a reference to the coroutine as a Job.

**/

fun CoroutineScope.launch(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> Unit

): Job

!!/**

* Creates a coroutine and returns its future result as

* an implementation of Deferred.

!*/

fun <T> CoroutineScope.async(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() "-> T

): Deferred<T>
Mostofthetimes…
Use launch in a top level coroutines scope
to create a new coroutine
Use async in a nested coroutines scope to
execute tasks in parallel
suspend fun topUsers(): List<User> {

val topUsers = api.fetchTopUsers()

storage.save(topUsers)

return topUsers

}1
Fireandforget
suspend fun topUsers(): List<User> = coroutineScope {

val topUsers = api.fetchTopUsers()

launch {

storage.save(topUsers)

}2

topUsers

}1
Fireandforget
!!/**

* !!...

* When any child coroutine in this scope fails,

* this scope fails and all the rest of the children are cancelled.

* This function returns as soon as the given block and

* all its children coroutines are completed.

* !!...

!*/

suspend fun <R> coroutineScope(block: suspend CoroutineScope.() "-> R): R
!!/**

* !!...

* When any child coroutine in this scope fails,

* this scope fails and all the rest of the children are cancelled.

* This function returns as soon as the given block and

* all its children coroutines are completed.

* !!...

!*/

suspend fun <R> coroutineScope(block: suspend CoroutineScope.() "-> R): R
suspend fun topUsers(): List<User> = coroutineScope {

val topUsers = api.fetchTopUsers()

launch {

storage.save(topUsers)

}2

topUsers

}1
Fireandforget
val saveScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

suspend fun topUsers(): List<User> {

val topUsers = api.fetchTopUsers()

saveScope.launch {

try {

storage.save(topUsers)

} catch (e: Exception) {

!//!!...

}

}2

return topUsers

}1
Fireandforget
fun load() {

viewModelScope.launch {

try {

updateUi(api.fetchTopUsers())

} catch (e: Exception) {

!//!!...

}

}

}
Precache
private val deferredData = viewModelScope.async {

api.fetchTopUsers()

}

fun load() {

viewModelScope.launch {

try {

updateUi(deferredData.await())

} catch (e: Exception) {

!//!!...

}

}

}
Precache
select
Initial loading Fast network Slow network
withTimeout(2000) {

updateUi(topUsers())7

}4
try {

withTimeout(2000) {

updateUi(topUsers())7

}4

} catch (e: TimeoutCancellationException) {

storage.load()"?.let {

updateUi(it)

}5

try {

updateUi(topUsers())

} catch (e: Exception)8{

!//ignored

}6

}7
try {

withTimeout(2000) {

updateUi(topUsers())

}4

} catch (e: TimeoutCancellationException) {

storage.load()"?.let {

updateUi(it)

}5

try {

updateUi(topUsers())

} catch (e: Exception)8{

!//ignored

}6

}7catch (e: Exception) {

val dataFromCache = storage.load()

if (dataFromCache "!= null) {

updateUi(dataFromCache)

} else {

showErrorMessage()

}1

}2
sealed class FetchResult<T> {

class FetchSuccess<T>(val data: T) : FetchResult<T>()

class FetchError(val error: Throwable) : FetchResult<Nothing>()

object Timeout : FetchResult<Nothing>()

}
val fetch = async {

try {

FetchSuccess(topUsers())

} catch (e: Exception) {

FetchError(e)

}1

}2
val fetch = async {

try {

FetchSuccess(topUsers())

} catch (e: Exception) {

FetchError(e)

}1

}2

val timeout = async {

delay(2000)

Timeout

}3
val fetch = async {

try {

FetchSuccess(topUsers())

} catch (e: Exception) {

FetchError(e)

}1

}2

val timeout = async {

delay(2000)

Timeout

}3

val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4
val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4
val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4

when (result) {

}b
val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4

when (result) {

is FetchSuccess "-> {

timeout.cancel()AAA

updateUi(result.data)

}5

}b
val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4

when (result) {

is FetchSuccess "-> {

timeout.cancel()AAA

updateUi(result.data)

}5

is Timeout "-> {

storage.load()"?.let {

updateUi(it)

}8

val fetchResult = fetch.await()

(fetchResult as? FetchSuccess)"?.let {

updateUi(it.data)

}9

}a

}b
val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4

when (result) {

is FetchSuccess "-> {

timeout.cancel()

updateUi(result.data)

}5

is Timeout "-> {

storage.load()"?.let {

updateUi(it)

}8

val fetchResult = fetch.await()

(fetchResult as? FetchSuccess)"?.let {

updateUi(it.data)

}9

}a

is FetchError "-> {

timeout.cancel()

val dataFromCache = storage.load()

if (dataFromCache "!= null) {

updateUi(dataFromCache)

} else {

showErrorMessage()

}6

}7

}b
val fetch = async {

try {

FetchSuccess(topUsers())

} catch (e: Exception) {

FetchError(e)

}1

}2

val timeout = async {

delay(2000)

Timeout

}3

val result = select<FetchResult<out List<UserStats"">>> {

fetch.onAwait { it }

timeout.onAwait { it }

}4

when (result) {

is FetchSuccess "-> {

timeout.cancel()

updateUi(result.data)

}5

is Timeout "-> {

storage.load()"?.let {

updateUi(it)

}8

val fetchResult = fetch.await()

(fetchResult as? FetchSuccess)"?.let {

updateUi(it.data)

}9

}a

is FetchError "-> {

timeout.cancel()

val dataFromCache = storage.load()

if (dataFromCache "!= null) {

updateUi(dataFromCache)

} else {

showErrorMessage()

}6

}7

}b
class RefreshStrategyTest {

private val updateUiCalls = mutableListOf<Pair<String, Long">>()

private val showErrorCalls = mutableListOf<Long>()

@Test

fun initialLoading() = runBlockingTest {

refresh(

storage = { null },

network = {

delay(1000)

"New data"

},

updateUi = { updateUiCalls.add(it to currentTime) },

showErrorMessage = { showErrorCalls.add(currentTime) }

)3

assertThat(updateUiCalls).containsExactly("New data" to 1000L)

assertThat(showErrorCalls).isEmpty()

}1

}2
@Test

fun initialLoading() = runBlockingTest {

refresh(

storage = { null },

network = {

delay(1000)

"New data"

},

updateUi = { updateUiCalls.add(it to currentTime) },

showErrorMessage = { showErrorCalls.add(currentTime) }

)3

assertThat(updateUiCalls).containsExactly("New data" to 1000L)

assertThat(showErrorCalls).isEmpty()

}1
@Test

fun initialLoading() = runBlockingTest {

refresh(

storage = { null },

network = {

delay(1000)

"New data"

},

updateUi = { updateUiCalls.add(it to currentTime) },

showErrorMessage = { showErrorCalls.add(currentTime) }

)3

assertThat(updateUiCalls).containsExactly("New data" to 1000L)

assertThat(showErrorCalls).isEmpty()

}1
@Test

fun initialLoading() = runBlockingTest {

refresh(

storage = { null },

network = {

delay(1000)

"New data"

},

updateUi = { updateUiCalls.add(it to currentTime) },

showErrorMessage = { showErrorCalls.add(currentTime) }

)3

assertThat(updateUiCalls).containsExactly("New data" to 1000L)

assertThat(showErrorCalls).isEmpty()

}1
@Test

fun dataAreLoadedIn5SecondsFromNetwork() = runBlockingTest {

refresh(

storage = { "Old data" },

network = {

delay(5000)

"New data"

},

updateUi = { updateUiCalls.add(it to currentTime) },

showErrorMessage = { showErrorCalls.add(currentTime) }

)

assertThat(updateUiCalls)

.containsExactly(

"Old data" to 2000L,

"New data" to 5000L

)

assertThat(showErrorCalls).isEmpty()

}
@Test

fun onNetworkErrorShowErrorIfCacheIsEmpty() = runBlockingTest {

refresh(

storage = { null },

network = {

delay(1000)

throw IOException()

},

updateUi = { updateUiCalls.add(it to currentTime) },

showErrorMessage = { showErrorCalls.add(currentTime) }

)

assertThat(updateUiCalls).isEmpty()

assertThat(showErrorCalls).containsExactly(1000L)

}
Wrappingup
Define main safe functions
Use launch in a top level coroutines scope
Use async in a nested coroutines scope
Never catch an exception inside a
coroutinesScope
Links&contacts
Coroutines on Android (part I): Getting the background
medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Async code using Kotlin Coroutines
proandroiddev.com/async-code-using-kotlin-coroutines-233d201099ff
Managing exceptions in nested coroutine scopes
proandroiddev.com/managing-exceptions-in-nested-coroutine-scopes-9f23fd85e61
@fabioCollini
linkedin.com/in/fabiocollini
github.com/fabioCollini
medium.com/@fabioCollini
THANKS
FOR YOUR
ATTENTION
QUESTIONS?
@fabioCollini

Contenu connexe

Tendances

Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POPNatasha Murashev
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingNatasha Murashev
 
Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)Natasha Murashev
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackGabor Varadi
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Fabio Collini
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaFabio Collini
 
Practical Protocols with Associated Types
Practical Protocols with Associated TypesPractical Protocols with Associated Types
Practical Protocols with Associated TypesNatasha Murashev
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - ComponentsVisual Engineering
 
Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2Jeado Ko
 
Funcitonal Swift Conference: The Functional Way
Funcitonal Swift Conference: The Functional WayFuncitonal Swift Conference: The Functional Way
Funcitonal Swift Conference: The Functional WayNatasha Murashev
 
Protocol Oriented MVVM - Auckland iOS Meetup
Protocol Oriented MVVM - Auckland iOS MeetupProtocol Oriented MVVM - Auckland iOS Meetup
Protocol Oriented MVVM - Auckland iOS MeetupNatasha Murashev
 
Android programming -_pushing_the_limits
Android programming -_pushing_the_limitsAndroid programming -_pushing_the_limits
Android programming -_pushing_the_limitsDroidcon Berlin
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascriptDoeun KOCH
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptVisual Engineering
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React AlicanteIgnacio Martín
 
LetSwift RxSwift 시작하기
LetSwift RxSwift 시작하기LetSwift RxSwift 시작하기
LetSwift RxSwift 시작하기Wanbok Choi
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftRodrigo Leite
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testingVisual Engineering
 

Tendances (19)

Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
 
Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)
 
droidparts
droidpartsdroidparts
droidparts
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-Stack
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
 
Practical Protocols with Associated Types
Practical Protocols with Associated TypesPractical Protocols with Associated Types
Practical Protocols with Associated Types
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - Components
 
Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2
 
Funcitonal Swift Conference: The Functional Way
Funcitonal Swift Conference: The Functional WayFuncitonal Swift Conference: The Functional Way
Funcitonal Swift Conference: The Functional Way
 
Protocol Oriented MVVM - Auckland iOS Meetup
Protocol Oriented MVVM - Auckland iOS MeetupProtocol Oriented MVVM - Auckland iOS Meetup
Protocol Oriented MVVM - Auckland iOS Meetup
 
Android programming -_pushing_the_limits
Android programming -_pushing_the_limitsAndroid programming -_pushing_the_limits
Android programming -_pushing_the_limits
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascript
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScript
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
 
LetSwift RxSwift 시작하기
LetSwift RxSwift 시작하기LetSwift RxSwift 시작하기
LetSwift RxSwift 시작하기
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 

Similaire à Managing parallelism using coroutines

Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeMacoscope
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
Scaladroids: Developing Android Apps with Scala
Scaladroids: Developing Android Apps with ScalaScaladroids: Developing Android Apps with Scala
Scaladroids: Developing Android Apps with ScalaOstap Andrusiv
 
How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF Luc Bors
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with EclipseEclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with EclipseHeiko Behrens
 
Threads, Queues, and More: Async Programming in iOS
Threads, Queues, and More: Async Programming in iOSThreads, Queues, and More: Async Programming in iOS
Threads, Queues, and More: Async Programming in iOSTechWell
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaFabio Collini
 
Is java8a truefunctionallanguage
Is java8a truefunctionallanguageIs java8a truefunctionallanguage
Is java8a truefunctionallanguageSamir Chekkal
 
Is java8 a true functional programming language
Is java8 a true functional programming languageIs java8 a true functional programming language
Is java8 a true functional programming languageSQLI
 
Code generation for alternative languages
Code generation for alternative languagesCode generation for alternative languages
Code generation for alternative languagesRafael Winterhalter
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 

Similaire à Managing parallelism using coroutines (20)

Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, Macoscope
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
 
How te bring common UI patterns to ADF
How te bring common UI patterns to ADFHow te bring common UI patterns to ADF
How te bring common UI patterns to ADF
 
Scaladroids: Developing Android Apps with Scala
Scaladroids: Developing Android Apps with ScalaScaladroids: Developing Android Apps with Scala
Scaladroids: Developing Android Apps with Scala
 
How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with EclipseEclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
 
Android crashcourse
Android crashcourseAndroid crashcourse
Android crashcourse
 
Threads, Queues, and More: Async Programming in iOS
Threads, Queues, and More: Async Programming in iOSThreads, Queues, and More: Async Programming in iOS
Threads, Queues, and More: Async Programming in iOS
 
Kitura Todolist tutorial
Kitura Todolist tutorialKitura Todolist tutorial
Kitura Todolist tutorial
 
Codemotion appengine
Codemotion appengineCodemotion appengine
Codemotion appengine
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
 
Is java8a truefunctionallanguage
Is java8a truefunctionallanguageIs java8a truefunctionallanguage
Is java8a truefunctionallanguage
 
Is java8 a true functional programming language
Is java8 a true functional programming languageIs java8 a true functional programming language
Is java8 a true functional programming language
 
Code generation for alternative languages
Code generation for alternative languagesCode generation for alternative languages
Code generation for alternative languages
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 

Plus de Fabio Collini

Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectFabio Collini
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureFabio Collini
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFabio Collini
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinFabio Collini
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFabio Collini
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFabio Collini
 
Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKFabio Collini
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternFabio Collini
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeFabio Collini
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMFabio Collini
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaFabio Collini
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Fabio Collini
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Fabio Collini
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Fabio Collini
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011Fabio Collini
 

Plus de Fabio Collini (16)

Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture project
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
 
Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUK
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVM
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011
 

Dernier

Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
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 GoalsJhone kinadey
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 

Dernier (20)

Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
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
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 

Managing parallelism using coroutines

  • 3. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.text) textView.text = "1" Thread.sleep(2000) textView.text = "2" Thread.sleep(2000) textView.text = "3" }1 }2
  • 4. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.text) textView.text = "1" Thread.sleep(2000) textView.text = "2" Thread.sleep(2000) textView.text = "3" }1 }2
  • 5. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.text) lifecycleScope.launch { textView.text = "1" delay(2000) textView.text = "2" delay(2000) textView.text = "3" }3 }1 }2
  • 6. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.text) lifecycleScope.launch { textView.text = "1" delay(2000) textView.text = "2" delay(2000) textView.text = "3" }3 }1 }2
  • 7. internal class AndroidDispatcherFactory : MainDispatcherFactory { override fun createDispatcher(allFactories: List<MainDispatcherFactory>) = HandlerContext(Looper.getMainLooper().asHandler(async = true), "Main") !//!!... }1 kotlinx-coroutines-android
  • 8. internal class AndroidDispatcherFactory : MainDispatcherFactory { override fun createDispatcher(allFactories: List<MainDispatcherFactory>) = HandlerContext(Looper.getMainLooper().asHandler(async = true), "Main") !//!!... }1 kotlinx-coroutines-android
  • 9. internal class HandlerContext private constructor( private val handler: Handler, private val name: String?, private val invokeImmediately: Boolean ) : HandlerDispatcher(), Delay { !//!!... override fun isDispatchNeeded(context: CoroutineContext): Boolean { return !invokeImmediately "|| Looper.myLooper() "!= handler.looper }2 override fun dispatch(context: CoroutineContext, block: Runnable) { handler.post(block) }3 !//!!... override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle { handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY)) return object : DisposableHandle { override fun dispose() { handler.removeCallbacks(block) }4 }5 }6 }7 kotlinx-coroutines-android
  • 10. internal class HandlerContext private constructor( private val handler: Handler, private val name: String?, private val invokeImmediately: Boolean ) : HandlerDispatcher(), Delay { !//!!... override fun isDispatchNeeded(context: CoroutineContext): Boolean { return !invokeImmediately "|| Looper.myLooper() "!= handler.looper }2 override fun dispatch(context: CoroutineContext, block: Runnable) { handler.post(block) }3 !//!!... override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle { handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY)) return object : DisposableHandle { override fun dispose() { handler.removeCallbacks(block) }4 }5 }6 }7 kotlinx-coroutines-android
  • 11. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.text) lifecycleScope.launch { textView.text = "1" delay(2000) textView.text = "2" delay(2000) textView.text = "3" }6 } }
  • 12. lifecycleScope.launch { textView.text = "1" delay(2000) textView.text = "2" delay(2000) textView.text = "3" }6
  • 13. lifecycleScope.launch { textView.text = "1" delay(2000) textView.text = "2" delay(2000) textView.text = "3" }6 val handler = Handler() textView.text = "1" handler.postDelayed({ textView.text = "2" handler.postDelayed({ textView.text = "3" }, 2000) }, 2000)
  • 15.
  • 16.
  • 17.
  • 18. interface StackOverflowService { @GET("/users") suspend fun fetchTopUsers(): List<User> @GET("/users/{userId}/badges") suspend fun fetchBadges( @Path("userId") userId: Int ): List<Badge> @GET("/users/{userId}/top-tags") suspend fun fetchTags( @Path("userId") userId: Int ): List<Tag> }
  • 19. class Storage { fun load(): List<User>? {3 val content = FileInputStream(getCacheFile()) .bufferedReader() .use { it.readText() } return parseData(content) }1 }2
  • 20. class Storage { fun load(): List<User>? = withContext(Dispatchers.IO) {3 val content = FileInputStream(getCacheFile()) .bufferedReader() .use { it.readText() } return parseData(content) }1 }2
  • 21. class Storage { suspend fun load(): List<User>? = withContext(Dispatchers.IO) { val content = FileInputStream(getCacheFile()) .bufferedReader() .use { it.readText() } return parseData(content) }1 }2
  • 22. viewModelScope.launch { !//!!... val dataFromCache = storage.load() val data = if (dataFromCache "!= null) { dataFromCache } else { val fetchedData = api.fetchTopUsers()[0] storage.save(fetchedData) fetchedData }3 updateUi(data) !//!!... }1
  • 23. viewModelScope.launch { !//!!... val dataFromCache = storage.load() val data = if (dataFromCache "!= null) { dataFromCache } else { val fetchedData = api.fetchTopUsers()[0] println("where am I executed?") storage.save(fetchedData) fetchedData }3 updateUi(data) !//!!... }1 main main io main main main io main io main main main main main
  • 24. viewModelScope.launch { !//!!... val data = withContext(Dispatchers.IO) { val dataFromCache = storage.load() if (dataFromCache "!= null) { dataFromCache } else { val fetchedData = api.fetchTopUsers()[0] println("where am I executed?") storage.save(fetchedData) fetchedData }3 } updateUi(data) !//!!... }1 main main io io io io io io io io io io io main main main
  • 26. suspend fun topUser(): User { return api.fetchTopUsers()[0] }1
  • 27. suspend fun topUser(): UserStats { val user = api.fetchTopUsers()[0] val badges = api.fetchBadges(user.id) val tags = api.fetchTags(user.id) return UserStats( user, badges, tags )2 }1
  • 28. suspend fun topUser(): UserStats { val user = api.fetchTopUsers()[0] val badges = api.fetchBadges(user.id) val tags = api.fetchTags(user.id) return UserStats( user, badges, tags )2 }1 fun topUser(): Single<UserStats> = api.fetchTopUsers() .map { users "-> users[0] } .flatMap { user "-> api.fetchBadges(user.id) .flatMap { badges "-> api.fetchTags(user.id).map { tags "-> UserStats(user, badges, tags) } } } RxJavaCoroutines
  • 29. Async
  • 30.
  • 31.
  • 32. suspend fun topUser(): UserStats { val user = api.fetchTopUsers()[0] val badges = api.fetchBadges(user.id) val tags = api.fetchTags(user.id) return UserStats( user, badges, tags )2 }1
  • 33. suspend fun topUser(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() } return UserStats( user, badges, tags )2 }1
  • 34. suspend fun topUser(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() } return UserStats( user, badges, tags )2 }1
  • 35. viewModelScope.launch {6 val duration = measureTimeMillis { coroutineScope { val v1 = async {7 Thread.sleep(1000) 1 }1 val v2 = async {8 Thread.sleep(1000) 2 }2 v1.await() + v2.await() }3 }4 println("Duration: $duration") }5 Duration: 2000 MainDispatcher Mainthread sleepsleep
  • 36. viewModelScope.launch(Dispatchers.IO) {6 val duration = measureTimeMillis { coroutineScope { val v1 = async {7 Thread.sleep(1000) 1 }1 val v2 = async {8 Thread.sleep(1000) 2 }2 v1.await() + v2.await() }3 }4 println("Duration: $duration") }5 Duration: 1000 Dispatchers.IO IOthread1 sleep IOthread2 sleep IOthreadn
  • 37. viewModelScope.launch {6 val duration = measureTimeMillis { coroutineScope { val v1 = async(Dispatchers.IO) {7 Thread.sleep(1000) 1 }1 val v2 = async(Dispatchers.IO) {8 Thread.sleep(1000) 2 }2 v1.await() + v2.await() }3 }4 println("Duration: $duration") }5 Duration: 1000 Dispatchers.IO IOthread1 sleep IOthread2 sleep IOthreadn
  • 38. viewModelScope.launch {6 val duration = measureTimeMillis { coroutineScope { val v1 = async {7 delay(1000) 1 }1 val v2 = async {8 delay(1000) 2 }2 v1.await() + v2.await() }3 }4 println("Duration: $duration") }5 Duration: 1000 MainDispatcher Mainthread delay delay
  • 39. !!/** * !!... * The resulting coroutine has a key difference compared with similar * primitives in other languages and frameworks: * it cancels the parent job (or outer scope) on failure * to enforce structured concurrency paradigm. * !!... !*/ fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> T ): Deferred<T>
  • 40. !!/** * !!... * The resulting coroutine has a key difference compared with similar * primitives in other languages and frameworks: * it cancels the parent job (or outer scope) on failure * to enforce structured concurrency paradigm. * !!... !*/ fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> T ): Deferred<T>
  • 41. !!/** * !!... * The resulting coroutine has a key difference compared with similar * primitives in other languages and frameworks: * it cancels the parent job (or outer scope) on failure * to enforce structured concurrency paradigm. * !!... !*/ fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> T ): Deferred<T>
  • 42. viewModelScope.launch { try { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() } updateUi(UserStats(user, badges, tags)) } catch (e: Exception) { showErrorMessage() }1 }2
  • 49. viewModelScope.launch { try { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() } updateUi(UserStats(user, badges, tags)) } catch (e: Exception) { showErrorMessage() }1 }2
  • 50. viewModelScope.launch { try { val user = api.fetchTopUsers()[0] val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } updateUi(UserStats(user, badgesDeferred.await(), tagsDeferred.await())) } catch (e: Exception) { showErrorMessage() }1 }2
  • 54.
  • 55. viewModelScope.launch { try { val user = api.fetchTopUsers()[0] val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } updateUi(UserStats(user, badgesDeferred.await(), tagsDeferred.await())) } catch (e: Exception) { showErrorMessage() }1 }2
  • 56. viewModelScope.launch { val user = api.fetchTopUsers()[0] val badgesDeferred = async { runCatching { api.fetchBadges(user.id) } } val tagsDeferred = async { runCatching { api.fetchTags(user.id) } } val badges = badgesDeferred.await() val tags = tagsDeferred.await() if (badges.isFailure "|| tags.isFailure) { showErrorMessage() }1 }2 In case of an error the other is not cancelled
  • 57. class MyClass(private val api: StackOverflowService) : CoroutineScope { override val coroutineContext: CoroutineContext get() = SupervisorJob() + Dispatchers.Main suspend fun loadData(): UserStats { val user = api.fetchTopUsers()[0] val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } val badges = badgesDeferred.await() val tags = tagsDeferred.await() return UserStats(user, badges, tags) }1 }2
  • 58. class MyClass(private val api: StackOverflowService) { private val scope = MainScope() suspend fun loadData(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() }3 return UserStats(user, badges, tags) }1 }2
  • 59. suspend fun loadData(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() }3 return UserStats(user, badges, tags) }1
  • 60. suspend fun loadData(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { try { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() } catch (e: Exception) { emptyList<Badge>() to emptyList<Tag>() }4 }3 return UserStats(user, badges, tags) }1
  • 65.
  • 66. suspend fun loadData(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = coroutineScope { try { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() } catch (e: Exception) { emptyList<Badge>() to emptyList<Tag>() }4 }3 return UserStats(user, badges, tags) }1
  • 67. suspend fun loadData(): UserStats { val user = api.fetchTopUsers()[0] val (badges, tags) = try { coroutineScope { val badgesDeferred = async { api.fetchBadges(user.id) } val tagsDeferred = async { api.fetchTags(user.id) } badgesDeferred.await() to tagsDeferred.await() }3 } catch (e: Exception) { emptyList<Badge>() to emptyList<Tag>() }4 return UserStats(user, badges, tags) }1
  • 68.
  • 70. !!/** * Launches a new coroutine without blocking the current thread and * returns a reference to the coroutine as a Job. **/ fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> Unit ): Job !!/** * Creates a coroutine and returns its future result as * an implementation of Deferred. !*/ fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> T ): Deferred<T>
  • 71. !!/** * Launches a new coroutine without blocking the current thread and * returns a reference to the coroutine as a Job. **/ fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> Unit ): Job !!/** * Creates a coroutine and returns its future result as * an implementation of Deferred. !*/ fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() "-> T ): Deferred<T>
  • 72. Mostofthetimes… Use launch in a top level coroutines scope to create a new coroutine Use async in a nested coroutines scope to execute tasks in parallel
  • 73. suspend fun topUsers(): List<User> { val topUsers = api.fetchTopUsers() storage.save(topUsers) return topUsers }1 Fireandforget
  • 74. suspend fun topUsers(): List<User> = coroutineScope { val topUsers = api.fetchTopUsers() launch { storage.save(topUsers) }2 topUsers }1 Fireandforget
  • 75. !!/** * !!... * When any child coroutine in this scope fails, * this scope fails and all the rest of the children are cancelled. * This function returns as soon as the given block and * all its children coroutines are completed. * !!... !*/ suspend fun <R> coroutineScope(block: suspend CoroutineScope.() "-> R): R
  • 76. !!/** * !!... * When any child coroutine in this scope fails, * this scope fails and all the rest of the children are cancelled. * This function returns as soon as the given block and * all its children coroutines are completed. * !!... !*/ suspend fun <R> coroutineScope(block: suspend CoroutineScope.() "-> R): R
  • 77. suspend fun topUsers(): List<User> = coroutineScope { val topUsers = api.fetchTopUsers() launch { storage.save(topUsers) }2 topUsers }1 Fireandforget
  • 78. val saveScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) suspend fun topUsers(): List<User> { val topUsers = api.fetchTopUsers() saveScope.launch { try { storage.save(topUsers) } catch (e: Exception) { !//!!... } }2 return topUsers }1 Fireandforget
  • 79. fun load() { viewModelScope.launch { try { updateUi(api.fetchTopUsers()) } catch (e: Exception) { !//!!... } } } Precache
  • 80. private val deferredData = viewModelScope.async { api.fetchTopUsers() } fun load() { viewModelScope.launch { try { updateUi(deferredData.await()) } catch (e: Exception) { !//!!... } } } Precache
  • 82. Initial loading Fast network Slow network
  • 84. try { withTimeout(2000) { updateUi(topUsers())7 }4 } catch (e: TimeoutCancellationException) { storage.load()"?.let { updateUi(it) }5 try { updateUi(topUsers()) } catch (e: Exception)8{ !//ignored }6 }7
  • 85. try { withTimeout(2000) { updateUi(topUsers()) }4 } catch (e: TimeoutCancellationException) { storage.load()"?.let { updateUi(it) }5 try { updateUi(topUsers()) } catch (e: Exception)8{ !//ignored }6 }7catch (e: Exception) { val dataFromCache = storage.load() if (dataFromCache "!= null) { updateUi(dataFromCache) } else { showErrorMessage() }1 }2
  • 86. sealed class FetchResult<T> { class FetchSuccess<T>(val data: T) : FetchResult<T>() class FetchError(val error: Throwable) : FetchResult<Nothing>() object Timeout : FetchResult<Nothing>() }
  • 87. val fetch = async { try { FetchSuccess(topUsers()) } catch (e: Exception) { FetchError(e) }1 }2
  • 88. val fetch = async { try { FetchSuccess(topUsers()) } catch (e: Exception) { FetchError(e) }1 }2 val timeout = async { delay(2000) Timeout }3
  • 89. val fetch = async { try { FetchSuccess(topUsers()) } catch (e: Exception) { FetchError(e) }1 }2 val timeout = async { delay(2000) Timeout }3 val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4
  • 90. val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4
  • 91. val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4 when (result) { }b
  • 92. val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4 when (result) { is FetchSuccess "-> { timeout.cancel()AAA updateUi(result.data) }5 }b
  • 93. val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4 when (result) { is FetchSuccess "-> { timeout.cancel()AAA updateUi(result.data) }5 is Timeout "-> { storage.load()"?.let { updateUi(it) }8 val fetchResult = fetch.await() (fetchResult as? FetchSuccess)"?.let { updateUi(it.data) }9 }a }b
  • 94. val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4 when (result) { is FetchSuccess "-> { timeout.cancel() updateUi(result.data) }5 is Timeout "-> { storage.load()"?.let { updateUi(it) }8 val fetchResult = fetch.await() (fetchResult as? FetchSuccess)"?.let { updateUi(it.data) }9 }a is FetchError "-> { timeout.cancel() val dataFromCache = storage.load() if (dataFromCache "!= null) { updateUi(dataFromCache) } else { showErrorMessage() }6 }7 }b
  • 95. val fetch = async { try { FetchSuccess(topUsers()) } catch (e: Exception) { FetchError(e) }1 }2 val timeout = async { delay(2000) Timeout }3 val result = select<FetchResult<out List<UserStats"">>> { fetch.onAwait { it } timeout.onAwait { it } }4 when (result) { is FetchSuccess "-> { timeout.cancel() updateUi(result.data) }5 is Timeout "-> { storage.load()"?.let { updateUi(it) }8 val fetchResult = fetch.await() (fetchResult as? FetchSuccess)"?.let { updateUi(it.data) }9 }a is FetchError "-> { timeout.cancel() val dataFromCache = storage.load() if (dataFromCache "!= null) { updateUi(dataFromCache) } else { showErrorMessage() }6 }7 }b
  • 96. class RefreshStrategyTest { private val updateUiCalls = mutableListOf<Pair<String, Long">>() private val showErrorCalls = mutableListOf<Long>() @Test fun initialLoading() = runBlockingTest { refresh( storage = { null }, network = { delay(1000) "New data" }, updateUi = { updateUiCalls.add(it to currentTime) }, showErrorMessage = { showErrorCalls.add(currentTime) } )3 assertThat(updateUiCalls).containsExactly("New data" to 1000L) assertThat(showErrorCalls).isEmpty() }1 }2
  • 97. @Test fun initialLoading() = runBlockingTest { refresh( storage = { null }, network = { delay(1000) "New data" }, updateUi = { updateUiCalls.add(it to currentTime) }, showErrorMessage = { showErrorCalls.add(currentTime) } )3 assertThat(updateUiCalls).containsExactly("New data" to 1000L) assertThat(showErrorCalls).isEmpty() }1
  • 98. @Test fun initialLoading() = runBlockingTest { refresh( storage = { null }, network = { delay(1000) "New data" }, updateUi = { updateUiCalls.add(it to currentTime) }, showErrorMessage = { showErrorCalls.add(currentTime) } )3 assertThat(updateUiCalls).containsExactly("New data" to 1000L) assertThat(showErrorCalls).isEmpty() }1
  • 99. @Test fun initialLoading() = runBlockingTest { refresh( storage = { null }, network = { delay(1000) "New data" }, updateUi = { updateUiCalls.add(it to currentTime) }, showErrorMessage = { showErrorCalls.add(currentTime) } )3 assertThat(updateUiCalls).containsExactly("New data" to 1000L) assertThat(showErrorCalls).isEmpty() }1
  • 100. @Test fun dataAreLoadedIn5SecondsFromNetwork() = runBlockingTest { refresh( storage = { "Old data" }, network = { delay(5000) "New data" }, updateUi = { updateUiCalls.add(it to currentTime) }, showErrorMessage = { showErrorCalls.add(currentTime) } ) assertThat(updateUiCalls) .containsExactly( "Old data" to 2000L, "New data" to 5000L ) assertThat(showErrorCalls).isEmpty() }
  • 101. @Test fun onNetworkErrorShowErrorIfCacheIsEmpty() = runBlockingTest { refresh( storage = { null }, network = { delay(1000) throw IOException() }, updateUi = { updateUiCalls.add(it to currentTime) }, showErrorMessage = { showErrorCalls.add(currentTime) } ) assertThat(updateUiCalls).isEmpty() assertThat(showErrorCalls).containsExactly(1000L) }
  • 102. Wrappingup Define main safe functions Use launch in a top level coroutines scope Use async in a nested coroutines scope Never catch an exception inside a coroutinesScope
  • 103. Links&contacts Coroutines on Android (part I): Getting the background medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb Async code using Kotlin Coroutines proandroiddev.com/async-code-using-kotlin-coroutines-233d201099ff Managing exceptions in nested coroutine scopes proandroiddev.com/managing-exceptions-in-nested-coroutine-scopes-9f23fd85e61 @fabioCollini linkedin.com/in/fabiocollini github.com/fabioCollini medium.com/@fabioCollini