5. WHAT IS THE PROBLEM
progress.visibility = View.VISIBLE
userService.doLoginAsync(username, password) { user ->
userService.requestCurrentFriendsAsync(user) { friends ->
val finalUser = user.copy(friends = friends)
toast("User ${finalUser.name} has ${finalUser.friends.size} friends")
progress.visibility = View.GONE
}
}
Sevil Guler - 12.04.2019
6. WHAT IS THE PROBLEM
1. Do the second friends request after the first one
2. Run both requests at the same time and find a way to synchronize the callback results.
Sevil Guler - 12.04.2019
7. WHAT IS THE PROBLEM
progress.visibility = View.VISIBLE
userService.doLoginAsync(username, password) { user ->
userService.requestCurrentFriendsAsync(user) { currentFriends ->
userService.requestSuggestedFriendsAsync(user) { suggestedFriends ->
val finalUser = user.copy(friends = currentFriends + suggestedFriends)
progress.visibility = View.GONE
}
}
}
Sevil Guler - 12.04.2019
8. WHAT ARE COROUTINES?
▸ Like threads but better
▸ asynchronous code sequentially
▸ can be run using the same thread
▸ light-weight
▸ the limit is almost infinite.
▸ Based on the idea of suspending functions
▸ Safe place where suspending functions will not block the current
thread.
Sevil Guler - 12.04.2019
9. WHAT ARE COROUTINES?
fun withCoroutines() = runBlocking {
repeat(100000) { counter ->
launch {
print(" $counter")
}
}
}
fun withThread() = runBlocking {
repeat(100000) { counter ->
thread {
print(" $counter")
}
}
}
Sevil Guler - 12.04.2019
10. WHAT ARE COROUTINES?
coroutine {
progress.visibility = View.VISIBLE
val user = suspended { userService.doLogin(username, password) }
val currentFriends = suspended { userService.requestCurrentFriends(user) }
val finalUser = user.copy(friends = currentFriends)
progress.visibility = View.GONE
}
Coroutine builder
Sevil Guler - 12.04.2019
11. SUSPENDING FUNCTIONS
▸ Block the execution of the coroutine
val user = suspended { userService.doLogin(username, password) }
val currentFriends = suspended { userService.requestCurrentFriends(user) }
▸ Can run on the same or a different thread
▸ Only run inside a coroutine or inside another suspending
function.
▸ suspend reserved word
Sevil Guler - 12.04.2019
12. SUSPENDING FUNCTIONS
suspend fun suspendingFunction() : Int {
// Long running task
return 0
}
coroutine {
progress.visibility = View.VISIBLE
….
}
PROBLEM
Sevil Guler - 12.04.2019
13. COROUTINE CONTEXT
▸ Set of rules and configurations
▸ Dispatcher
✴ Explicitly: we manually set the dispatcher that will be used
✴ By the coroutine scope
Sevil Guler - 12.04.2019
19. HOW TO RUN COROUTINES - COROUTINES BUILDER
▸ runBlocking
▸ blocks the current thread
▸ very helpful to test suspending tasks
fun testSuspendingFunction() = runBlocking {
val res = suspendingTask1()
assertEquals(0, res)
}
Sevil Guler - 12.04.2019
20. HOW TO RUN COROUTINES - COROUTINES BUILDER
LAUNCH
GlobalScope.launch {
…..
}
▸ launch
▸ main builder
▸ won’t block the current thread
▸ always needs a scope
▸ returns a Job
Sevil Guler - 12.04.2019
21. JOBS HAVE A COUPLE OF INTERESTING FUNCTIONS
▸ job.join
▸ job.cancel
val job = GlobalScope.launch(Dispatchers.Main) {
doCoroutineTask()
val res1 = suspendingTask1()
val res2 = suspendingTask2()
process(res1, res2)
}
job.join()
Sevil Guler - 12.04.2019
22. ASYNC
GlobalScope.launch(Dispatchers.Main) {
val user = withContext(Dispatchers.IO) { userService.doLogin(username, password) }
val currentFriends = withContext(Dispatchers.IO{ userService.requestCurrentFriends(user) }
val suggestedFriends = withContext(Dispatchers.IO { userService.requestSuggestedFriends(user) }
val finalUser = user.copy(friends = currentFriends + suggestedFriends)
}
Sevil Guler - 12.04.2019
23. ASYNC
GlobalScope.launch(Dispatchers.Main) {
val user = withContext(Dispatchers.IO) { userService.doLogin(username, password) }
val currentFriends = async(Dispatchers.IO) { userService.requestCurrentFriends(user) }
val suggestedFriends = async(Dispatchers.IO) { userService.requestSuggestedFriends(user) }
val finalUser =currentFriends.await()+ suggestedFriends.await()
}
Sevil Guler - 12.04.2019
24. ASYNC
▸ several background tasks in parallel
▸ not a suspending function itself
▸ returns a specialized job that is called Deferred
▸ await(), which is the blocking one
Sevil Guler - 12.04.2019
27. IMPLEMENT COROUTINESCOPE
▸ override the coroutineContext
▸ configure: the dispatcher, and the job
▸ to identify the default dispatcher that the
coroutines will use
▸ cancel all pending coroutines at any moment
▸ plus(+) operation -> CombinedContext
the job is used as the parent of the coroutines created from this scope.
Dispatchers.Main + Dispatchers.IO == Dispatchers.IO
Sevil Guler - 12.04.2019
28. IMPLEMENT COROUTINESCOPE
class MainActivity : AppCompatActivity(), CoroutineScope {
private lateinit var job : Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
job = Job()
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
}
Sevil Guler - 12.04.2019
30. EXCEPTIONS
▸ An exception other than CancellationException in a coroutine
scope cancels its parent.
▸ CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
▸ If we want cancellation to be propagated only downwards we can
use SupervisorJob or supervisorScope.
Sevil Guler - 12.04.2019