Slide 1

Slide 1 text

Using Kotlin Coroutines on Android June 8th, 2019

Slide 2

Slide 2 text

Hi, I’m Adora Software Engineer Creator of AdoraHack Women Techmakers Ajah Ambassador FSDLagos & Android Ngr Co-organizer Twitter, @theadoranwodo

Slide 3

Slide 3 text

What we want to do val order = fetchOrderDetails() textView.text = order.code Never block the main thread

Slide 4

Slide 4 text

The Reality val order = fetchOrderDetails() // NetworkOnMainThreadException textView.text = order.code

Slide 5

Slide 5 text

The Reality thread{ val order = fetchOrderDetails() textView.text = order.code // CalledFromWrongThreadException }

Slide 6

Slide 6 text

A Solution fetchOrderDetails{ order -> textView.text = order.code } Lots of memory leaks Also very difficult to read. We don’t want callback hell :(

Slide 7

Slide 7 text

A Nightmare fetchOrderDetails{ order -> textView.text = order.code someDbTransaction{ db_res -> anotherCallback{ res -> saveToFile{ res -> // and it continues ... } } } }

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Introducing Coroutines

Slide 10

Slide 10 text

Let’s try Coroutines Coroutines are lightweight threads. Anti callbacks. Write code sequentially. Makes code easier to read. We can have much more coroutines as opposed to threads. Coroutine mechanism: suspend and resume. Declare a very heavy task as a suspend function

Slide 11

Slide 11 text

Let’s try Coroutines - suspend function suspend fun fetchOrderDetails(): Order { … } suspend fun doSomething() { val order = fetchOrderDetails() textView.text = order.code }

Slide 12

Slide 12 text

Where can I call a suspend function? In another suspend function’ In coroutine builders (e,g, async, launch) In suspend lambdas In coroutine scopes

Slide 13

Slide 13 text

Coroutines with WorkManager

Slide 14

Slide 14 text

WorkManager WorkManager provides a unified API for background work taking battery and compatibility across Android versions to account. - Worker - CoroutineWorker - RxWorker - ListenableWorker

Slide 15

Slide 15 text

WorkManager & Coroutines dependencies { def work_version = "2.0.1" // Kotlin + coroutines implementation "androidx.work:work-runtime-ktx:$work_version" } Snippet from: https://developer.android.com/topic/libraries/architecture/workmanager/advanced/coroutineworker.html

Slide 16

Slide 16 text

WorkManager & Coroutines class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override val coroutineContext = Dispatchers.IO override suspend fun doWork(): Result = coroutineScope { val jobs = (0 until 100).map { async { downloadSynchronously("https://www.google.com") } } // awaitAll will throw an exception if a download fails, which CoroutineWorker will treat as a failure jobs.awaitAll() Result.success() } } Snippet from: https://developer.android.com/topic/libraries/architecture/workmanager/advanced/coroutineworker.html

Slide 17

Slide 17 text

Coroutines with Architecture components + Coroutine Leaks

Slide 18

Slide 18 text

Coroutine leaks To deal with Coroutine leaks, Kotlin introduced Coroutine scopes. A scope is a way to keep track of coroutines. Coroutines run in a scope and the scope has the ability to cancel all the coroutines inside it. Use scopes to avoid leaks. Structured concurrency.

Slide 19

Slide 19 text

Coroutine Scopes //viewmodel scope implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$view_model_scope_version” //lifecycle scope implementation “androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_scope_version” //livedata implementation “androidx.lifecycle:lifecycle-livedata-ktx:$livedata_scope_version”

Slide 20

Slide 20 text

viewModelScope Coroutines are useful here when there is work that should be done only when the ViewModel is active. class MyViewModel: ViewModel() { init { viewModelScope.launch { // Coroutine that will be canceled when the ViewModel is cleared. } } }

Slide 21

Slide 21 text

LifecycleScope A LifecycleScope is defined for each Lifecycle object. Any coroutine launched in this scope is canceled when the Lifecycle is destroyed. You can access the Lifecycle’s CoroutineScope either via lifecycle.coroutineScope or lifecycleOwner.lifecycleScope properties. LifecycleOwner could be an Activity or a Fragment.

Slide 22

Slide 22 text

liveData LiveData is an observable value holder for UI It is now interoperable with Coroutines Compute value in Coroutine and serve as LiveData

Slide 23

Slide 23 text

liveData val user = liveData { val data = database.loadUser() // loadUser is a suspend function. emit(data) } - Use the liveData builder function to call a suspend function asynchronously, - use emit() to emit the result:

Slide 24

Slide 24 text

Testing Coroutines

Slide 25

Slide 25 text

Testing Coroutines kotlinx-coroutines-test : Experimental coroutines API It’s not coupled to any testing libraries, you can use any library.

Slide 26

Slide 26 text

Testing Coroutines val testDispatcher = TestCoroutineDispatcher() val testScope = TestCoroutineScope(testDispatcher) @Before fun setup(){ Dispatchers.setMain(testDispatcher) } @After fun tearDown(){ Dispatchers.resetMain() testScope.cleanupTestCoroutines() }

Slide 27

Slide 27 text

Testing Coroutines @get: Rule val testCoroutineRule = TestCoroutineRule() @Test fun testSomething() = testCoroutineRule.runBlockingTest { // test code here }

Slide 28

Slide 28 text

Resources https://kotlinlang.org/docs/reference/coroutines-overview.html https://codelabs.developers.google.com/codelabs/kotlin-coroutines/ https://developer.android.com/kotlin/coroutines

Slide 29

Slide 29 text

Thanks! www.adoranwodo.com www.adorahack.com Twitter: @theadoranwodo GitHub: adoranwodo