Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin Coroutines and Flow - DevFest KL 2019

Hassan Abid
December 07, 2019

Kotlin Coroutines and Flow - DevFest KL 2019

Coroutines were added to Kotlin in version 1.3 and since then they became a popular choice for android developers to simplify code that executes asynchronously. Coroutines are lightweight threads and on android they help to solve the primary problems of long running tasks that might block the main thread and also providing safety for offloading network or disk operations from the main thread. This talk will show you how to get started with coroutines in Android, and also an introduction to asynchronous data streams with Flow . This talk also covers how coroutines work with architecture components

Hassan Abid

December 07, 2019
Tweet

More Decks by Hassan Abid

Other Decks in Programming

Transcript

  1. Improving app performance with Kotlin Coroutines Hassan Abid - GDE

    Android 
 Twitter / Instagram @hassanabidpk
  2. –Android Developers Website A coroutine is a concurrency design pattern

    that you can use on Android to simplify code that executes asynchronously.
  3. Why we need Asynchronous? • Main Thread has to update

    screen every 16ms which is 60Hz (frames per second) fetchImage(url)
  4. Async Programming Techniques • Threading • Callbacks • Futures, Promises

    • Reactive Extensions (Rx) • Coroutines (new)
  5. // Async callbacks networkRequest { result -> // Successful network

    request databaseSave(result) { rows -> // Result saved } } // The same code with coroutines val result = networkRequest() // Successful network request databaseSave(result) // Result saved
  6. Manage Long running Tasks suspend fun fetchDocs() { // Dispatchers.Main

    val result = get("https://developer.android.com") // Dispatchers.IO for `get` show(result) // Dispatchers.Main } suspend fun get(url: String) = withContext(Dispatchers.IO) { /* ... */ }
  7. CoroutineScope • Defines a scope for new coroutines • CoroutinesScope

    stops/cancels coroutines execution when user leaves a content area with your app e.g activity, fragment • It should be implemented on entities with a well defined lifecycle (activity, fragment)
  8. CoroutineScope class MyActivity : AppCompatActivity(), CoroutineScope by MainScope() { override

    fun onDestroy() { cancel() // cancel is extension on CoroutineScope } /* * Note how coroutine builders are scoped: if activity is destroyed or any of the * launched coroutines * in this method throws an exception, then all nested coroutines are cancelled. */ fun showSomeData() = launch { // <- extension on current activity, launched in the main thread // ... here we can use suspending functions or coroutine builders with other dispatchers draw(data) // draw in the main thread } }
  9. CoroutineContext • Every coroutine in Kotlin has a context that

    is represented by an instance of CoroutineContext interface • Coroutine context is immutable, but you can add elements to a context using plus operator, • It stays on same dispatcher and avoids switching threads
  10. CoroutineDispatcher •The coroutine context includes a coroutine dispatcher that determines

    what thread or threads the corresponding coroutine uses for its execution.
 
 
 
 
 scope.launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher // code }
  11. Dispatchers suspend fun fetchDocs() { // Dispatchers.Main val result =

    get("developer.android.com") // Dispatchers.Main show(result) // Dispatchers.Main } suspend fun get(url: String) = // Dispatchers.Main withContext(Dispatchers.IO) { // Dispatchers.IO (main-safety block) /* perform network IO here */ // Dispatchers.IO (main-safety block) } // Dispatchers.Main }
  12. Dispatchers •Dispatchers.Main : Main Android Thread •Dispatchers.IO : For Network

    and Database (Room) calls •Dispatchers.Default : For CPU-intensive tasks
  13. Start a coroutine •launch : Doesn’t return result to the

    caller •async : Returns a result with suspend function await
  14. example : launch returns a Job without result fun onDocsNeeded()

    { val job = viewModelScope.launch { // Dispatchers.Main fetchDocs() // Dispatchers.Main (suspend function call) } job.cancel() }
  15. example : async (Parallel Decomposition) suspend fun fetchTwoDocs() = coroutineScope

    { val deferredOne = async { fetchDoc(1) } val deferredTwo = async { fetchDoc(2) } deferredOne.await() deferredTwo.await() } returns a Deffered
  16. Structured Concurrency • Coroutines are always related to local scope

    in your app UI Element, Activity • Structured concurrency requires that launch is invoked in a Scope which is interface implement by your life-time limited object So it can be cancelled / stopped / resumed
  17. Parallel Decomposition suspend fun loadAndCombine(name1: String, name2: String): Image {

    val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await())
 }
  18. CoroutineScope with Android Arch Components •Associate CoroutineScope implementations with a

    lifecycle component • Helps in avoiding Memory Leak • Hint : ViewModel?
  19. Coroutines with Android Arch components • viewModelScope use androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01 or

    higher. • lifeCycleScope use androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01 or higher. • liveData use androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01 or higher.
  20. Life-cycle aware coroutines scope: viewModelScope class MyViewModel: ViewModel() { init

    { viewModelScope.launch { // Coroutine that will be canceled when the ViewModel is cleared. } } }
  21. Life-cycle aware coroutines scope: lifeCycleScope class MyFragment: Fragment() { override

    fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { val params = TextViewCompat.getTextMetricsParams(textView) val precomputedText = withContext(Dispatchers.Default) { PrecomputedTextCompat.create(longTextContent, params) } TextViewCompat.setPrecomputedText(textView, precomputedText) } } }
  22. class MyFragment: Fragment { init { lifecycleScope.launchWhenStarted { try {

    // Call some suspend functions. } finally { // This line might execute after Lifecycle is DESTROYED. if (lifecycle.state >= STARTED) { // Here, since we've checked, it is safe to run any // Fragment transactions. } } } } } Special cases
  23. Life-cycle aware coroutines scope: liveData val user: LiveData<Result> = liveData

    { emit(Result.loading()) try { emit(Result.success(fetchUser())) } catch(ioException: Exception) { emit(Result.error(ioException)) } } fun <T> liveData( context: CoroutineContext = EmptyCoroutineContext,..)
  24. Suspending Over Views • Blog Posts by Chris Banes https://medium.com/

    androiddevelopers/suspending-over- views-19de9ebd7020 • Example : https://medium.com/androiddevelopers/ suspending-over-views-example-260ce3dc9100
  25. –Kotlin Docs There are two ways you can deal with

    streams of data in coroutines: the Flow API and the Channel API.
  26. Channels • The channel represents a hot stream of values.

    • synchronization primitives • You shall use a channel when you need to send data from one coroutine to another coroutine in the same or in a different process
  27. Flow • Flow is a cold stream that give us

    same behaviour as that of Observable and Flowable in RxJava • Suspending functions asynchronously returns a single value, but how can we return multiple asynchronously computed values? This is where Kotlin Flows come in.
  28. fun foo(): Flow<Int> = flow { // flow builder for

    (i in 1..3) { delay(100) // pretend we are doing something useful here emit(i) // emit next value } } fun main() = runBlocking<Unit> { // Launch a concurrent coroutine to check if the main thread is blocked launch { for (k in 1..3) { println("I'm not blocked $k") delay(100) } } // Collect the flow foo().collect { value -> println(value) } } Flow : Example
  29. Flow : Advantages • Flow automatically close the stream of

    data • Flows won’t block the UI thread - they are non-blocking yet sequential • Flow supports a large set of operators to transform your data
  30. Kotlin Conf 19 Updates • Flow Talk https://kotlinconf.com/talks/6-dec/131903 • COROUTINES!

    https://kotlinconf.com/talks/5-dec/126674 • STORE4 - MIGRATING A LIBRARY FROM RXJAVA TO COROUTINES https://kotlinconf.com/talks/6-dec/126904
  31. References •Kotlin Conf : •Android Dev Summit 2019 Talk https://www.youtube.com/

    watch?v=B8ppnjGPAGE •Docs : https://kotlinlang.org/docs/reference/coroutines/ flow.html •Blog : https://medium.com/androiddevelopers/lessons- learnt-using-coroutines-flow-4a6b285c0d06
  32. Coroutines for Reactive Streams kotlinx-coroutines-reactive -- utilities for Reactive Streams

    kotlinx-coroutines-reactor -- utilities for Reactor kotlinx-coroutines-rx2 -- utilities for RxJava 2.x
  33. Summary •Coroutines are great - Start using them •If you

    are already using RxJava, you are FINE. You can slowly migrate to Coroutines.
  34. Resources (Videos) •Android Suspenders (Android Dev Summit 2019) https:// www.youtube.com/watch?v=EOjq4OIWKqM

    •Understanding Coroutines on Android (Google IO 19) https://www.youtube.com/watch?v=BOHK_w09pVA
  35. Resources (Docs) •Kotlin Docs : https://kotlinlang.org/docs/reference/ coroutines-overview.html •Android + Coroutines

    Docs : https:// developer.android.com/kotlin/coroutine •Coroutines + Android Arch components : https:// developer.android.com/topic/libraries/architecture/ coroutines