Slide 1

Slide 1 text

Coroutines 101 A Newbie guide to coroutines @viradiya_sagar

Slide 2

Slide 2 text

What is Coroutine? Coroutines are lightweight threads which allows to write async code in sequential manner.

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

What is Coroutine? 1. Coroutines are like lightweight threads. 2. Allows to write async code in sequential manner.

Slide 5

Slide 5 text

Why Async?

Slide 6

Slide 6 text

Main thread (UI thread) View Inflation Measure + Layout Draw Many more things... Your code

Slide 7

Slide 7 text

Refresh Rate 60 HZ ~16 ms

Slide 8

Slide 8 text

Refresh Rate 90 HZ ~12 ms

Slide 9

Slide 9 text

Refresh Rate 120 HZ ~8.3 ms

Slide 10

Slide 10 text

How Async ?

Slide 11

Slide 11 text

Callback Display list of tweets 1. Get token 2. Get list of tweets

Slide 12

Slide 12 text

fun getToken(cb: (Token) -> Unit) { //Makes a request for token, invokes callback when done //returns immediately } fun getTweets(token: Token, cb: (List) -> Unit) { //Makes a request for tweets, invokes callback when done //returns immediately }

Slide 13

Slide 13 text

fun displayTweets() { getToken { token -> getTweets(token) { tweets -> //Consume list of tweets } } }

Slide 14

Slide 14 text

Callback Display list of tweets 1. Get token 2. Get list of tweets 3. Process tweets

Slide 15

Slide 15 text

fun getToken(cb: (Token) -> Unit) { //Makes a request for token, invokes callback when done //returns immediately } fun getTweets(token: Token, cb: (List) -> Unit) { //Makes a request for tweets, invokes callback when done //returns immediately } fun processTweets(tweets: List, cb: (List) -> Unit) { //Process list of tweet, invoke callback when done //returns immediately }

Slide 16

Slide 16 text

fun displayTweets() { getToken { token -> getTweets(token) { tweets -> processTweets(tweets) { tweets -> //Consume tweets } } } }

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Reactive programming fun displayTweets() { val disposable = getToken() .flatMap(token: Token) { return getTweets(token) } .map(tweets: List) { //Perform processing } .subscribe( tweets -> { //Consume tweets }, throwable -> { //Handle error } ) }

Slide 19

Slide 19 text

Coroutines way to Async suspend fun getToken(): Token { //Get token from n/w call return token } suspend fun getTweet(token: Token): List { //Get tweets using token from n/w call return tweets } suspend fun processTweet(tweets: List): List { //Perform processing on list return updatedTweets }

Slide 20

Slide 20 text

suspend fun displayTweets() { val token = getToken() val tweets = getTweets(token) val updatedTweets = processTweets(tweets) //Consume updatedTweets }

Slide 21

Slide 21 text

Coroutines 1. Coroutines are like light weight threads. 2. It allows you to write async code in sequential manner.

Slide 22

Slide 22 text

fun main() = runBlocking { repeat(100_000) { // launch a lot of coroutines launch { delay(1000L) print(".") } } }

Slide 23

Slide 23 text

Suspending function Non blocking function Suspends the execution of coroutine and resumes execution. Concept of continuation.

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

suspend fun displayTweets() { val token = getToken() val tweets = getTweets(token) val updatedTweets = processTweets(tweets) //Consume updatedTweets }

Slide 26

Slide 26 text

ync suspend fun getToken(): Token { //Get token from n/w call return token } suspend fun getTweet(token: Token): List { //Get tweets using token from n/w call return tweets } suspend fun processTweet(tweets: List): List { //Perform processing on list return updatedTweets }

Slide 27

Slide 27 text

Concept of continuation void getToken(Continuation continuation) { //Get token from n/w call continuation.resume(token) } void getTweets(Token token, Continuation> continuation) { //Get tweets using token from n/w call continuation.resume(tweets) } void processTweets(List tweets, Continuation> continuation) { //Perform processing on list continuation.resume(updatedTweets) }

Slide 28

Slide 28 text

interface Continuation { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }

Slide 29

Slide 29 text

Coroutine scope Lifecycle of coroutine Scope encapsulate coroutine context.

Slide 30

Slide 30 text

public interface CoroutineScope { public val coroutineContext: CoroutineContext }

Slide 31

Slide 31 text

GlobalScope.launch { // launch new coroutine in background and continue delay(1000L) // non-blocking delay for 1 second (default time unit is ms) println("Hello World!") // print after delay }

Slide 32

Slide 32 text

class MainActivity : AppCompatActivity(), CoroutineScope { private lateinit var job: Job override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) job = Job() runTask() ... } fun runTask() { launch { //Perform task here } } override fun onDestroy() { job.cancel() super.onDestroy() } }

Slide 33

Slide 33 text

Coroutine context Job Dispatcher Coroutine name Exception handler

Slide 34

Slide 34 text

Coroutine builders Launch Async runBlocking

Slide 35

Slide 35 text

Launch fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job

Slide 36

Slide 36 text

Launch val job = GlobalScope.launch { // launch new coroutine in background and continue delay(1000L) // non-blocking delay for 1 second (default time unit is ms) println("Hello World!") // print after delay } . . . job.cancel()

Slide 37

Slide 37 text

Async - Await Use for concurrent tasks which are independent of each other. Await - Suspending function to get result from async.

Slide 38

Slide 38 text

Async fun CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred

Slide 39

Slide 39 text

suspend fun displayTweets() { val token = getToken() val tweets1 : Deferred> = async(Dispatchers.Default) { getTweets(token, "#IPL") } val tweets2 : Deferred> = async(Dispatchers.Default) { getTweets(token, "#EPL") } val updatedTweets = processTweets(tweets1.await() + tweets2.await()) //Consume updatedTweets }

Slide 40

Slide 40 text

runBlocking Blocks the current thread until coroutine completes. Useful in unit testing.

Slide 41

Slide 41 text

fun main() = runBlocking { // start main coroutine GlobalScope.launch { // launch new coroutine in background and continue delay(1000L) println("World!") } println("Hello,") // main coroutine continues here immediately }

Slide 42

Slide 42 text

Coroutine dispatcher Dispatcher.Default Dispatcher.IO Dispatcher.Unconfined Dispatcher.Main (Need separate dependency for each UI framework)

Slide 43

Slide 43 text

coroutineScope withContext Scoping functions

Slide 44

Slide 44 text

coroutineScope Designed for parallel decomposition of work. suspend fun doThingsParallelly() = coroutineScope { val result1 = async { //Task1 } val result2 = async { //Task2 } result1.await() + result2.await() }

Slide 45

Slide 45 text

withContext Mostly for thread switching. launch { mainScope -> . . . val result = withContext(Dispatcher.Default) { //Perform heavy task here } . . . }

Slide 46

Slide 46 text

Summary 1. Need for async programming. 2. Ways to achieve async programming. 3. Suspending functions. 4. Coroutines scope. 5. Coroutines context. 6. Coroutines builders. 7. Coroutines dispatchers. 8. Scoping functions.

Slide 47

Slide 47 text

Resources Kotlin official doc - https://kotlinlang.org/docs/reference/coroutines/basics.html Blog post - https://antonioleiva.com/coroutines/ Videos ● KotlinConf 2018 - Exploring Coroutines in Kotlin by Venkat Subramariam https://youtu.be/jT2gHPQ4Z1Q ● KotlinConf 2017 - Introduction to Coroutines by Roman Elizarov https://youtu.be/_hfBv0a09Jc ● KotlinConf 2018 - Android Suspenders by Chris Banes https://youtu.be/P7ov_r1JZ1g

Slide 48

Slide 48 text

Thank you !

Slide 49

Slide 49 text

Questions ?