
안드로이드 디벨로퍼: https://developer.android.com/kotlin/coroutines?hl=ko
코루틴 Cancellation URL
안드로이드 비동기 프로그래밍에 권장되는 솔루션입니다.
LoginRepository 에서는 Coroutine I/O 쓰레드를 사용한다.
LoginViewModel 에서는 Coroutine Default 쓰레드를 사용한다.
class LoginRepository(...) {
...
suspend fun makeLoginRequest(
jsonBody: String
): Result<LoginResponse> {
// Move the execution of the coroutine to the I/O dispatcher
return withContext(Dispatchers.IO) {
// Blocking network request code
}
}
}
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun makeLoginRequest(username: String, token: String) {
viewModelScope.launch {
val jsonBody = "{ username: \"$username\", token: \"$token\"}"
val result = try {
loginRepository.makeLoginRequest(jsonBody)
} catch(e: Exception) {
Result.Error(Exception("Network request failed"))
}
when (result) {
is Result.Success<LoginResponse> -> // Happy path
else -> // Show error in UI
}
}
}
}
invoke call suspend resume
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) { /* ... */ }
withContext 는 정지함수이므로 위의 get함수도 정지함수가 된다.
네트워크 호출을 10번 호출하는 경우 withContext를 한번만 사용하면 콜백 구현 필요없이 스레드를 한번만 전환 하도록 Kotlin 에 지시할 수있습니다.
withContext를 여러번 호출해도 동일한 디스페처에 유지되고 스레드가 전환되지 않습니다. Default, IO 간의 전환을 최적화 합니다.
다른 코루틴 스코프를 정의했을 때 awiat()를 호출하여 두 async 작업이 모두 완료되도록 보장 합니다.
suspend fun fetchTwoDocs() =
coroutineScope {
val deferredOne = async { fetchDoc(1) }
val deferredTwo = async { fetchDoc(2) }
deferredOne.await()
deferredTwo.await()
val deferreds = listOf( // fetch two docs at the same time
async { fetchDoc(1) }, // async returns a result for the first doc
async { fetchDoc(2) } // async returns a result for the second doc
)
deferreds.awaitAll() // use awaitAll to wait for both network requests
}
CoroutineScope 는 launch 또는 async를 사용하여 만든 코루틴을 추적합니다.
scope.cancel()을 호출하여 취소할 수 있습니다.
ViewModel 에는 ViewModelScope가 있고 Lifecycle에는 LifecycleScope가 있습니다.
그러나 CoroutineScope를 만들어 앱의 특정 레이어에서 코루틴 수명 주기를 제어해야하면 아래와같이 만들면 됩니다.
class ExampleClass {
// Job and Dispatcher are combined into a CoroutineContext which
// will be discussed shortly
val scope = CoroutineScope(Job() + Dispatchers.Main)
fun exampleMethod() {
// Starts a new coroutine within the scope
scope.launch {
// New coroutine that can call suspend functions
fetchDocs()
}
}
fun cleanUp() {
// Cancel the scope to cancel ongoing coroutines work
scope.cancel()
}
}
Job은 코루틴의 핸들입니다.
launch 또는 async로 만드는 각 코루틴은 코루틴을 고유하게 식별하고 수명 주기를 관리하는 Job 인스턴스를 반환합니다. Job을 CoroutineScope에 전달하여 코루틴의 수명주기를 추가로 관리할 수 있습니다.
class ExampleClass {
...
fun exampleMethod() {
// Handle to the coroutine, you can control its lifecycle
val job = scope.launch {
// New coroutine
}
if (...) {
// Cancel the coroutine started above, this doesn't affect the scope
// this coroutine was launched in
job.cancel()
}
}
}
class ExampleClass {
val scope = CoroutineScope(Job() + Dispatchers.Main)
fun exampleMethod() {
// Starts a new coroutine on Dispatchers.Main as it's the scope's default
val job1 = scope.launch {
// New coroutine with CoroutineName = "coroutine" (default)
}
// Starts a new coroutine on Dispatchers.Default
val job2 = scope.launch(Dispatchers.Default + CoroutineName("BackgroundCoroutine")) {
// New coroutine with CoroutineName = "BackgroundCoroutine" (overridden)
}
}
}