fun login(username: String, password: String): User
Slide 8
Slide 8 text
fun login(username: String, password: String): User
fun getOrders(userId: String): List
Slide 9
Slide 9 text
fun login(username: String, password: String): User
fun getOrders(userId: String): List
fun fetchUserOrders() {
val user = login(username, password)
val orders = getOrders(user.userId)
showUserOrders(orders)
}
Slide 10
Slide 10 text
fun fetchUserOrders(username: String, password: String) {
login(username, password) {
user -> getOrders(user.userId) {
orders -> showUserOrders(orders)
}
}
}
Callbacks
Slide 11
Slide 11 text
RxJava Example
fun fetchUserOrders(username: String, password: String) {
login(username, password)
.flatMap { user → getOrders(user.userId) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.main())
.subscribe({orders -> showUserOrders(orders)})
}
Slide 12
Slide 12 text
What we actually want
fun fetchUserOrders() {
val user = login(username, password)
val orders = getOrders(user.userId)
showUserOrders(orders)
}
Slide 13
Slide 13 text
What is a coroutine
A lightweight thread that can run a computation without
blocking.
It can be suspended and resumed at a later time.
SUSPENDING FUNCTION
A function that can be started, paused and resumed at a
later time.
Slide 22
Slide 22 text
SUSPENDING FUNCTION
A function that can be started, paused and resumed at a
later time.
● Can only be called from a coroutine and from another
suspending function
Slide 23
Slide 23 text
SUSPENDING FUNCTION
A function that can be started, paused and resumed at a
later time.
● Can only be called from a coroutine and from another
suspending function
● Can take parameters and have return types.
Slide 24
Slide 24 text
Suspending function syntax
suspend fun doHeavyComputation(param: Int): Int {
//heavy computation body
}
Slide 25
Slide 25 text
fun doHeavyComputation(param: Int, callback:Continuation)
Slide 26
Slide 26 text
fun doHeavyComputation(param: Int, callback:Continuation)
interface Continuation {
val context: CoroutineContext
fun resume(value: T) { }
fun resumeWithException(exception: Throwable)
}
Slide 27
Slide 27 text
COROUTINE CONTEXT
Slide 28
Slide 28 text
COROUTINE CONTEXT
Coroutine Context: A map from CoroutineContext.Key to
CoroutineContext.Element
Slide 29
Slide 29 text
COROUTINE CONTEXT
Coroutine Context: A map from CoroutineContext.Key to
CoroutineContext.Element
● Job
Slide 30
Slide 30 text
COROUTINE CONTEXT
Coroutine Context: A map from CoroutineContext.Key to
CoroutineContext.Element
● Job
● ContinuationInterceptor
Slide 31
Slide 31 text
COROUTINE CONTEXT
Coroutine Context: A map from CoroutineContext.Key to
CoroutineContext.Element
● Job
● ContinuationInterceptor
● CoroutineName
Slide 32
Slide 32 text
COROUTINE CONTEXT
Coroutine Context: A map from CoroutineContext.Key to
CoroutineContext.Element
● Job
● ContinuationInterceptor
● CoroutineName
● CoroutineExceptionHandler
Slide 33
Slide 33 text
COROUTINE DISPATCHERS
Determines which thread or threads the coroutine uses for
its execution.
It can confine a coroutine to specific thread, a thread pool
or let it run unconfined.
Async...
● You need to call await() on the returned Deferred
object to get the eventual result.
Slide 48
Slide 48 text
Async...
● You need to call await() on the returned Deferred
object to get the actual result.
● Async..await be used to run tasks in parallel.
Slide 49
Slide 49 text
Async...
● You need to call await() on the returned Deferred
object to get the actual result.
● Async..await be used to run tasks in parallel.
● Deferred is also a job.
Slide 50
Slide 50 text
Parallel operations example
suspend fun loadImage(url: String): Bitmap
Slide 51
Slide 51 text
Parallel operations example
suspend fun loadImage(url: String): Bitmap
fun combineImages()= GlobalScope.launch(Dispatchers.MAIN) {
val image1 = async(Dispatchers.DEFAULT) { loadImage(“..”) }
val image2 = async(Dispatchers.DEFAULT) { loadImage(“..”) }
imageView.setImageBitmap(
combine(image1.await(), image2.await())
)
}
Slide 52
Slide 52 text
Parallel operations example
suspend fun loadImage(url: String): Bitmap
fun combineImages()= GlobalScope.launch(Dispatchers.MAIN) {
val image1 = async(Dispatchers.DEFAULT) { loadImage(“..”) }
val image2 = async(Dispatchers.DEFAULT) { loadImage(“..”) }
imageView.setImageBitmap(
combine(image1.await(), image2.await())
)
}
Slide 53
Slide 53 text
private fun loadData() = GlobalScope.launch(Dispatchers.MAIN) {
}
Slide 54
Slide 54 text
private fun loadData() = GlobalScope.launch(Dispatchers.MAIN) {
view.showLoading()
}
Slide 55
Slide 55 text
private fun loadData() = GlobalScope.launch(Dispatchers.MAIN) {
view.showLoading()
val result = withContext(Dispatcher.IO) { dataRepository.loadData() }
}
Slide 56
Slide 56 text
private fun loadData() = GlobalScope.launch(Dispatchers.MAIN) {
view.showLoading()
val result = withContext(Dispatcher.IO) { dataRepository.loadData() }
}
Slide 57
Slide 57 text
private fun loadData() = GlobalScope.launch(Dispatchers.MAIN) {
view.showLoading()
val result = withContext(Dispatcher.IO) { dataRepository.loadData() }
view.hideLoading()
}
Slide 58
Slide 58 text
private fun loadData() = GlobalScope.launch(Dispatchers.MAIN) {
view.showLoading()
val result = withContext(Dispatcher.IO) { dataRepository.loadData() }
view.hideLoading()
view.showData(result)
}
Slide 59
Slide 59 text
get user orders revisited
fun fetchUserOrders() = GlobalScope.launch(Dispatchers.MAIN){
val user = withContext(Dispatchers.IO) { login(username, password) }
val orders = withContext(Dispatchers.IO) { getOrders(user.userId) }
showUserOrders(orders)
}
Slide 60
Slide 60 text
CANCELLING A JOB
Call cancel() on the Job object returned from a single
coroutine
Slide 61
Slide 61 text
CANCELLING A JOB
Call cancel() on the Job object returned from a single
coroutine
val job = GlobalScope.launch {
// background operation
}
job.cancel()
Slide 62
Slide 62 text
Child Coroutines inherits the context of parent coroutines
Slide 63
Slide 63 text
Child Coroutines inherits the context of parent coroutines
val job = GlobalScope.launch {
val task1 = async {...}
val task2 = async {...}
val result = task1.await() + task2.await()
}
Slide 64
Slide 64 text
Child Coroutines inherits the context of parent coroutines
val job = GlobalScope.launch {
val task1 = async {...}
val task2 = async {...}
val result = task1.await() + task2.await()
}
job.cancel() //cancels the execution of parent and child
coroutines
Slide 65
Slide 65 text
class MainActivity: AppCompatActivity() {
private fun loadData() {
}
private fun doAnimations() {
}
}
Slide 66
Slide 66 text
class MainActivity: AppCompatActivity(), CoroutineScope {
private fun loadData() {
}
private fun doAnimations() {
}
}
Slide 67
Slide 67 text
class MainActivity: AppCompatActivity(), CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.DEFAULT
private fun loadData() {
}
private fun doAnimations() {
}
}
Slide 68
Slide 68 text
class MainActivity: AppCompatActivity(), CoroutineScope {
private lateint var job
override val coroutineContext: CoroutineContext
get() = Dispatchers.DEFAULT
private fun loadData() {
}
private fun doAnimations() {
}
}
Slide 69
Slide 69 text
class MainActivity: AppCompatActivity(), CoroutineScope {
private lateint var job
override val coroutineContext: CoroutineContext
get() = Dispatchers.DEFAULT
override fun onCreate(savedInstanceState: Bundle?) {
job = Job()
}
private fun loadData() {
}
}
Slide 70
Slide 70 text
class MainActivity: AppCompatActivity(), CoroutineScope {
private lateint var job
override val coroutineContext: CoroutineContext
get() = Dispatchers.DEFAULT
override fun onCreate(savedInstanceState: Bundle?) {
job = Job()
}
override fun onDestroy() {
job.cancel()
}
}
Slide 71
Slide 71 text
class MainActivity: AppCompatActivity(), CoroutineScope {
private lateint var job
override val coroutineContext: CoroutineContext
get() = Dispatchers.DEFAULT + job
override fun onCreate(savedInstanceState: Bundle?) {
job = Job()
}
override fun onDestroy() {
job.cancel()
}
}
Slide 72
Slide 72 text
class MainActivity: AppCompatActivity(), CoroutineScope {
private lateint var job
override val coroutineContext: CoroutineContext
get() = Dispatchers.DEFAULT + job
override fun onCreate(savedInstanceState: Bundle?) {
job = Job()
}
override fun onDestroy() {
job.cancel()
}
}
Slide 73
Slide 73 text
HANDLING EXCEPTIONS
Exceptions can be handled in the conventional way by
wrapping code in try..catch block.
Slide 74
Slide 74 text
HANDLING EXCEPTIONS
Exceptions can be handled in the conventional way by
wrapping code in try..catch block.
Coroutine builders either propagate exceptions automatically
or expose it to the user
Slide 75
Slide 75 text
fun main(args: Array) = runBlocking {
val job = GlobalScope.launch {
println("Throwing exception from launch")
throw IndexOutOfBoundsException()
}
job.join()
println("Joined failed job")
val deferred = GlobalScope.async {
println("Throwing exception from async")
throw ArithmeticException()
}
try {
deferred.await()
println("Unreached")
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
}
Slide 76
Slide 76 text
fun main(args: Array) = runBlocking {
val job = GlobalScope.launch {
println("Throwing exception from launch")
throw IndexOutOfBoundsException()
}
job.join()
println("Joined failed job")
val deferred = GlobalScope.async {
println("Throwing exception from async")
throw ArithmeticException()
}
try {
deferred.await()
println("Unreached")
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
}
Slide 77
Slide 77 text
fun main(args: Array) = runBlocking {
val job = GlobalScope.launch {
println("Throwing exception from launch")
throw IndexOutOfBoundsException() // exception printed out to the console
}
job.join()
println("Joined failed job")
val deferred = GlobalScope.async {
println("Throwing exception from async")
throw ArithmeticException() //nothing happens, waits for user to call await
}
try {
deferred.await()
println("Unreached")
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
}
Slide 78
Slide 78 text
fun main(args: Array) = runBlocking {
val job = GlobalScope.launch {
println("Throwing exception from launch")
throw IndexOutOfBoundsException() // exception printed out to the console
}
job.join()
println("Joined failed job")
val deferred = GlobalScope.async {
println("Throwing exception from async")
throw ArithmeticException() //nothing happens, waits for user to call await
}
try {
deferred.await()
println("Unreached") // will not be printed
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
}
● Provides a way to transfer a stream of values
● They can be shared between different coroutines and have
default capacity of 1.
Slide 84
Slide 84 text
● Provides a way to transfer a stream of values
● They can be shared between different coroutines and have
default capacity of 1.
● Channels implements two interfaces, the SendChannel
and the ReceiveChannel
Slide 85
Slide 85 text
public interface SendChannel {
suspend fun send(element: E)
fun offer(element: E)
fun close(cause: Throwable? = null) : Boolean
}
Slide 86
Slide 86 text
public interface SendChannel {
suspend fun send(element: E)
fun offer(element: E)
fun close(cause: Throwable? = null) : Boolean
}
public interface ReceiveChannel {
suspend fun receive(): E
fun close(throwable? = null): Boolean
}
Slide 87
Slide 87 text
USAGE
val channel = Channel() //default size is 1
Slide 88
Slide 88 text
USAGE
val channel = Channel() //default size is 1
GlobalScope.launch {
for (i in 1..5) {
delay(100)
channel.send(i)
}
channel.close()
}
Slide 89
Slide 89 text
USAGE
val channel = Channel() //default size is 1
GlobalScope.launch {
for (i in 1..5) {
delay(100)
channel.send(i)
}
channel.close()
}
Slide 90
Slide 90 text
USAGE
val channel = Channel() //default size is 1
GlobalScope.launch {
for (i in 1..5) {
delay(100)
channel.send(i)
}
channel.close()
}
GlobalScope.launch {
for (i in channel)
println(“received: $i”)
}
Slide 91
Slide 91 text
Producer, Publisher and Actor
Slide 92
Slide 92 text
Producer, Publisher and Actor
● produce {} - streams of event
Slide 93
Slide 93 text
Producer, Publisher and Actor
● produce {} - streams of event
● publish {} - becomes a stream of event upon
subscription
Slide 94
Slide 94 text
Producer, Publisher and Actor
● produce {} - streams of event
● publish {} - becomes a stream of event upon
subscription
● actor - implements SendChannel
Slide 95
Slide 95 text
Sending a message to multiple listeners
● BroadCastChannel
Slide 96
Slide 96 text
Sending a message to multiple listeners
● BroadCastChannel
● ConflatedBroadCastChannel
Slide 97
Slide 97 text
RxJava InterOp
Call `openForSubscription` extension function on Observable
/ Flowable
Slide 98
Slide 98 text
RxJava InterOp
Call `openForSubscription` extension function on Observable
/ Flowable
Flowable.range(1, 5).openForSubscription().consume {
for (i in this) println(i) // ReceiveChannel
}
Slide 99
Slide 99 text
RxJava InterOp
Call `openForSubscription` extension function on Observable
/ Flowable
Flowable.range(1, 5).openForSubscription().consume {
for (i in this) println(i) // ReceiveChannel
}
Or
Flowable.range(1, 5).consumeEach{
println($it)
}
Slide 100
Slide 100 text
Convert Job to Rx Completable
val job = GlobalScope.launch {
heavyComputation()
}
Slide 101
Slide 101 text
Convert Job to Rx Completable
val job = GlobalScope.launch {
heavyComputation()
}
job.asCompletable(Dispatchers.Default)
Slide 102
Slide 102 text
Convert Job to Rx Completable
val job = GlobalScope.launch {
heavyComputation()
}
job.asCompletable(Dispatchers.Default).subscribe({..},{..
})
Slide 103
Slide 103 text
Convert Deferred to Rx Single
suspend fun loadData(): List { }
val deferred = GlobalScope.async { loadData() }
deferred.asSingle(Dispatcher.DEFAULT).subscribe({
//on success
}, {
//on error
})