Kotlin Coroutines is a powerful threading library for Kotlin, released by JetBrains in 2018. At The New York Times, we recently migrated our core libraries and parts of our News app from RxJava to Kotlin Coroutines. In this talk we’ll share lessons learned and best practices to understand, migrate to, and use Kotlin Coroutines & Flows.
In this presentation, you will learn:
What Coroutines are and how they function
How to use Kotlin Coroutines & Flows (with real world examples and demos)
Where and why you should use Coroutines & Flows in your app
How to avoid the pitfalls of Coroutines
Kotlin Coroutines vs. RxJava
Lessons learned from migrating to Kotlin Coroutines from RxJava in large legacy projects & libraries
By the end of this talk, you will be able to apply Kotlin Coroutines to your own app, run the provided sample code yourself, and convince your team to give Kotlin Coroutines a try!
3. Topics
1. What are Coroutines and how do they function
2. Coroutines & Flows in the Real World
3. Pitfalls & Troubleshooting
4. Migrating from RxJava to Coroutines
?
4. Topics
1. What are Coroutines and how do they function
2. Coroutines & Flows in the Real World
3. Pitfalls & Troubleshooting
4. Migrating from RxJava to Coroutines
?
5. How Coroutines Work
• Light weight functions that can run on a pool of threads
• Cheap to create (great parallel performance) and cancellable
• Suspend will yield the thread and can start on same / another thread
10. • Can switch Dispatchers to change thread pools
How Coroutines Work
Main Thread
Dispatchers.IO
11. • Can switch Dispatchers to change thread pools
How Coroutines Work
Main Thread
Dispatchers.IO
….
launch(Dispatcher.IO)
12. • Can switch Dispatchers to change thread pools
How Coroutines Work
Main Thread
Dispatchers.IO
!
13. • Can switch Dispatchers to change thread pools
How Coroutines Work
Main Thread
Dispatchers.IO
!
withContext(Dispatcher.Main)
14. • Can switch Dispatchers to change thread pools
How Coroutines Work
Main
Thread
Dispatchers.IO
15. Coroutine Basics – How does it work?
Why does the coroutine code look synchronous?
• All code within a suspend function or a CoroutineScope is synchronous
• Asynchronous threading when you launch a new coroutine
16. Coroutine Basics – How does it work?
Why does the coroutine code look synchronous?
• All code within a suspend function or a CoroutineScope is synchronous
• Asynchronous threading when you launch a new coroutine
17. Coroutine Basics – Rule #1
All suspend functions MUST be called from another suspend function or a
CoroutineScope launch / async or runBlocking.
Why?
18. Why do I need CoroutineScope?
CoroutineScope
• Wrapper around CoroutineContext(s) for parent / child coroutines
CoroutineContext
• Helps determine what Dispatcher you’re running on
• Contains the Job of your coroutines
• Handles Exceptions and Cancellations
19. Topics
1. What are Coroutines and how do they function
2. Coroutines & Flows in the Real World
3. Pitfalls & Troubleshooting
4. Migrating from RxJava to Coroutines
37. State Flow
• Best for UI binding (Ex: Result<Loading, Error,
Value>)
• Always needs initial value
• Can create StateFlow from Flow
38. Shared Flow
• Useful for results emitted from multiple places
• No initial value
• StateFlow is a SharedFlow + initial value
• Can control cache / replay
42. Callback Flows & suspendCancellable
• Can emit updates from object callbacks within its scope
• Use callbackFlow when you get more than one update
• Use suspendCancellable for a single update
• Example usage: waiting for location, callbacks from listeners (error /
success) when have to include object listeners and override their
callback methods (ex: Third Party API bridge into coroutines / flows)
51. Topics
1. What are Coroutines and how do they
function
2. Coroutines & Flows in the Real World
3. Pitfalls & Troubleshooting
4. Migrating from RxJava to Coroutines
52. Global Scope
• Use for launching top level coroutines that run for the entire application
lifecycle ONLY
• Up to developer to cancel individual Jobs
• Can cause resource / memory leaks
53. Working with Lifecycle Scope
• Cancellation of coroutines launched in scope is important
56. Working with Lifecycle Scope
• ViewModelScope automatically handles cancellation
• UI LifecycleScope is more difficult
57. Working with Lifecycle Scope
• lifecycleScope.launchWhenStarted, <StateFlow>.collectAsState.
58. Working with Lifecycle Scope
• lifecycleScope.launchWhenStarted, <StateFlow>.collectAsState
• Instead use: Lifecycle.repeatOnLifecycle / Lifecycle.flowWithLifecycle
59. Blocking Threads
• RULE: All suspend functions should not block the caller thread
• Anytime you have a blocking function, make sure to wrap it in a non-
blocking suspend function
• Use withContext(Dispatcher.IO)
61. Unit Testing
• Pass CoroutineDispatcher in constructors
• Use TestCoroutineDispatcher
• Use runBlockingTest
• Debugging
62. Topics
1. What are Coroutines and how do they function
2. Coroutines & Flows in the Real World
3. Pitfalls & Troubleshooting
4. Migrating from RxJava to Coroutines
63. The New York Times w/ RxJava & Coroutines
NYT Android News App
After 3 months of work,
we migrated our core
feature libraries from
RxJava into Kotlin
Coroutines and integrated
them with our News app.
Our app and libraries are
faster and more reliable
because of it.
64. Performance Comparisons
An example of results from one of our larger core library migrations
This sample should not be taken as a generalization. Performance gains may vary based
on uses of RxJava vs. Kotlin Coroutines & Flow
67. Basic Observable flow, flowOf
Observable updated from callbacks callbackFlow / suspendCancellable
Observable updated from different locations SharedFlow
Observable updated from different locations that
can have an initial state
StateFlow
Observable à Flow
RxJava
Observable
Coroutines
Flow
68. Backwards Compatibility Workarounds
• https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive
• Includes Kotlin extension functions and utilities to convert from RxJava
to Coroutines & back
73. Improvements from migrations for our teams
• Sped up feature creation
• Improved app & library performance
• Used Room / Lifecycle w/ Coroutines
• Cleaned up app architecture patterns
• Simplified unit testing