[Android Docs] Kotlin Coroutines on Android(2)

이전 이야기 - Kotlin Coroutines on Android(1)

Use coroutines for main-safety

We consider a function main-safe when it doesn't block UI updates on the main thread.
The makeLoginRequest function is not main-safe, as calling makeLoginRequest from the main thread does block the UI.
Use the withContext() function from the coroutines library to move the execution of a coroutine to a different thread:

우리는 메인스레드에서 ui업데이트가 차단되지 않 함수를 메인 세이프 함수라고 간주한다. {it은 function을 가리키고 여기서 consider은 a를 b라고 간주한다.}

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
        }
    }
}

withContext(Dispatchers.IO) moves the execution of the coroutine to an I/O thread, making our calling function main-safe and enabling the UI to update as needed.
makeLoginRequest is also marked with the suspend keyword. This keyword is Kotlin's way to enforce a function to be called from within a coroutine.

withContext 함수는 i/o스레드로 메인세이프 함수와 가능한 ui 업데이트를 생성하면서 코루틴 실행을 이동시킨다 makeLoginRequest도 suspend 키워드가 붙여있다. 해당 키워드는 코루틴 내부에서 함수가 호출되도록 강제하는 방법이다.

In the following example, the coroutine is created in the LoginViewModel. As makeLoginRequest moves the execution off the main thread,
the coroutine in the login function can be now executed in the main thread:

예제에서 코루틴은 LoginViewModel에서 생성되었다. makeLoginRequest가 메스레드 밖의 실행으로 이동하므로, 로그인 함수에서 코루틴은 이제 메인스레드 안에서 실행될 수 있다.

class LoginViewModel(
    private val loginRepository: LoginRepository
): ViewModel() {

    fun login(username: String, token: String) {

        // Create a new coroutine on the UI thread
        viewModelScope.launch {
            val jsonBody = "{ username: \"$username\", token: \"$token\"}"

            // Make the network call and suspend execution until it finishes
            val result = loginRepository.makeLoginRequest(jsonBody)

            // Display result of the network request to the user
            when (result) {
                is Result.Success<LoginResponse> -> // Happy path
                else -> // Show error in UI
            }
        }
    }
}

Note that the coroutine is still needed here, since makeLoginRequest is a suspend function,
and all suspend functions must be executed in a coroutine.
This code differs from the previous login example in a couple of ways:

makeLoginRequest 함수가 서스펜드 함수이기때문에 코루틴은 여전히 여기에 필요하며 모든 서스펜드 함수는 코루틴 내에서 실행되어야한다. 해당 코드는 이전 로그인 예제와 몇몇 차이가 있다.

  • launch doesn't take a Dispatchers.IO parameter. When you don't pass a Dispatcher to launch, any coroutines launched from viewModelScope run in the main thread.

런처는 디스패쳐 io파라미터를 사용하지 않는다. 런처로 디스패처를 전달하지 않을 때 뷰모델 스코프에서 실행된 코루틴들은 메인 스레드에서 실행된다.

  • The result of the network request is now handled to display the success or failure UI.

네트워크 요청 결과는 성공 혹은 실패 ui를 표시하도록 처리된다.

The login function now executes as follows:

  • The app calls the login() function from the View layer on the main thread.

앱은 로그인 함수를 메인스레드에서 뷰 레이아웃로부터 호출한다.

  • launch creates a new coroutine to make the network request on the main thread, and the coroutine begins execution.

런처는 메인스레드에서 네트워크 요처을 만들기 위해 새로운 코루틴을 생성한다 그리고 코루틴은 실행을 시작한다.

  • Within the coroutine, the call to loginRepository.makeLoginRequest() now suspends further execution
    of the coroutine until the withContext block in makeLoginRequest() finishes running.

코루틴 내부에서 loginRepository.makeLoginRequest()함수 호출은 makeLoginRequest() 함수 실행이 끝날때까지 추가 실행을 정지한다.

  • Once the withContext block finishes, the coroutine in login() resumes execution on the main thread with
    the result of the network request.

withContext 블록이 완료되면 로그인 함수에 있는 코루틴은 네트워크 요청의 결과와 메인스레드에서 실행을 재개한다.

★ Note: To communicate with the View from the ViewModel layer, use LiveData as recommended in the Guide to app architecture. When following this pattern, the code in the ViewModel is executed on the main thread, so you can call MutableLiveData's setValue() function directly.

뷰모델 레이어로부터 뷰와 통신하기 위해서는 라이브 데이터 사용을 권장한다.

Handling exceptions

To handle exceptions that the Repository layer can throw, use Kotlin's built-in support for exceptions.
In the following example, we use a try-catch block:

저장소 레이어에서 발생할 수 있는 예외를 다루기 위해서 코루틴에서 자체 지원하는 예외를 사용한다.

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
            }
        }
    }
}

In this example, any unexpected exception thrown by the makeLoginRequest() call is handled as an error in the UI.

해당 예제에서 makeLoginRequest() 함수 호출에 의해 발생되는 예외상황은 ui에서 에러로서 처리된다.


📗 단어 정리 📘

영어한글
consider간주하다

좋은 웹페이지 즐겨찾기