We face challenges with threading on a daily basis. There are too many ways to do it, too many options. Do I use an AsyncTaks, a Runnable or RxJava, what do I do? All these available APIs lead to an almost inevitable callback hell.
This talk will introduce Kotlin Coroutines as an alternative solution to this problem. We’re going to explore simple ways to perform long-running tasks and computation heavy operations in an asynchronous way, without freezing or crashing your app, while keeping the code clean and readable at the same time.
2. Agenda
1. What are coroutines
2. Coroutines in Android
3. Why use coroutines
3. What are coroutines
“coroutines are computations that can be suspended
without blocking a thread.”
● Not a new concept, introduced in the `60s
● Adopted by multiple programming languages
● Coroutines in Kotlin:
○ minimal low-level APIs in its standard library
○ kotlinx.coroutines libraries
4. What are coroutines
● Terminology :
○ Coroutine
○ Suspending function
○ Coroutine builder
○ Continuation
○ Coroutine context
5. Coroutine
● An instance of suspendable computation
● Similar to a thread (light-weight thread)
● Also similar to a future or promise
6. Suspending functions
● Functions which suspend a coroutine execution
● suspend keyword
● Suspending functions can be used:
○ Inside other functions marked with the suspend keyword
○ Inside a coroutine
● suspendCoroutine
○ Bridge between coroutine suspension and callback world
7. Suspending functions
/**
* Obtains the current continuation instance
* inside suspend functions and suspends
* currently running coroutine.
*/
suspend inline fun <T> suspendCoroutine(
crossinline block: (Continuation<T>) -> Unit): T =
8. Suspending functions
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resume(value: T)
public fun resumeWithException(exception: Throwable)
}
9. Suspending functions
suspend fun firstFun(): ResultType = suspendCoroutine { continuation ->
// do some heavy work
// resume the coroutine continuation with a result:
continuation.resume(result)
// or if there was an error:
continuation.resumeWithException(exception)
}
10. Coroutine builders
● Functions which start a coroutine
● Bridge between non-coroutine & coroutine world
● Most common coroutine builders:
○ launch
○ async
○ withContext
33. asyncOperation(object: Callback {
fun onSuccess(result) {
anotherOne(result, object: Callback {
fun onSuccess(secondResult){
lastOne(secondResult, object: Callback {
fun onSuccess(thirdResult){
useResult(thirdResult)
}
fun onError(exception){
handleError(exception)
}
})
}
fun onError(exception){
handleError(exception)
}
})
}
fun onError(exception) {
handleError(exception)
}
})
34. Why use coroutines
● Asynchronous code sequentially
● No more callback-hell
● “It’s experimental, we can’t use it!”
● “We already have so many options, why use this one?!”
35. Why use coroutines
val blurAsyncTask = object : AsyncTask<Uri, Int, Uri>() {
override fun doInBackground(vararg params: Uri?): Uri {
return blurImage(params)
}
override fun onPostExecute(result: Uri?) {
viewBinding.image.setImageURI(result)
viewModel.uploadImage(result)
}
}
blurAsyncTask.execute(imageUri)
36. Why use coroutines
private val executors = Executors.newCachedThreadPool()
fun uploadImage(imageUri: Uri) {
executors.execute {
val imageFile = File(imageUri.path)
val imageFilePart = createImagePart(imageFile)
restApiService.uploadBlurredImage(imageFilePart).enqueue(Callback<> {
override fun onResponse(response: Response<>) {
// use the response to update state, etc.
}
override fun onFailure(t: Throwable) {
}
})
}
}