이 포스팅은 아래 구글 코드랩을 개인 학습용으로 정리한 글입니다.
// 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
kotlinx-coroutines-core
- Main interface for using coroutines in Kotlin
kotlinx-coroutines-android
- Support for the Android Main thread in coroutines
The starter app already includes the dependencies in build.gradle
// Slow request with callbacks
@UiThread
fun makeNetworkRequest() {
// The slow network request runs on another thread
slowFetch { result ->
// When the result is ready, this callback will get the result
show(result)
}
// makeNetworkRequest() exits after calling slowFetch without waiting for the result
}
this code is annotated with @UiThread
-> it must run fast enough to execute on the main thread
However, since slowFetch will take seconds or even minutes to complete
-> the main thread can't wait for the result.
The show(result) callback allows slowFetch to run on a background thread and return the result when it's ready.
callbacks don't allow the use of some language features (ex. exceptions)
The keyword suspend:
Kotlin's way of marking a function, or function type, available to coroutines.
it suspends execution until the result is ready then it resumes where it left off with the result.
While it's suspended waiting for a result, it unblocks the thread that it's running on so other functions or coroutines can run.
// Slow request with coroutines
@UiThread
suspend fun makeNetworkRequest() {
// slowFetch is another suspend function
// instead of blocking the main thread,
// makeNetworkRequest will `suspend` until the result is ready
val result = slowFetch()
// continue to execute after the result is ready
show(result)
}
// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }
- The suspend keyword doesn't specify the thread code runs on.
**- Suspend functions may run on a background thread or the main thread
// Request data from network and save it to database with coroutines
// Because of the @WorkerThread, this function cannot be called on the
// main thread without causing an error.
@WorkerThread
suspend fun makeNetworkRequest() {
// slowFetch and anotherFetch are suspend functions
val slow = slowFetch()
val another = anotherFetch()
// save is a regular function and will block this thread
database.save(slow, another)
}
// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }
// anotherFetch is main-safe using coroutines
suspend fun anotherFetch(): AnotherResult { ... }
For coroutines started by the UI, it is typically correct to start them on Dispatchers.Main
- Dispatchers.Main: the main thread on Android
Since a ViewModel coroutine almost always updates the UI on the main thread,
starting coroutines on the main thread saves you extra thread switches.
⚡A coroutine started on the Main thread can switch dispatchers any time after it's started.
-> can easily switch threads at any time and pass results back to the original thread
dependencies {
...
// replace x.x.x with latest version
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:x.x.x"
}
/**
* Wait one second then update the tap count.
*/
private fun updateTaps() {
// TODO: Convert updateTaps to use coroutines
tapCount++
BACKGROUND.submit {
Thread.sleep(1_000)
_taps.postValue("$tapCount taps")
}
}
import java.util.concurrent.Executors
/**
* An executor service that can run [Runnable]s off the main thread.
*/
val BACKGROUND = Executors.newFixedThreadPool(2)