코루틴(Coroutine)
코루틴은 Kotlin에서 지원하는 비동기 처리 기술이다.
코루틴은 장기 실행 작업을 관리하는데 도움을 주며, 기능은 다음과 같다.
- 경량( Lightweight ): 코루틴은 실행 중인 스레드를 차단하지 않는 정지(suspend)를 지원하므로 단일 스레드에서 많은 코루틴을 실행할 수 있고, 동시 작업을 진행하면서 차단보다 메모리를 절약할 수 있다.
suspend fun func()
- 메모리 누수 감소: 구조화된 동시 실행을 사용하여 범위 내에서 작업을 실행한다.
- CoroutineScope에서만 새 코루틴을 시작할 수 있다.
- 기본 제공 취소 지원: 취소는 실행 중인 코루틴 계층 구조를 통해 자동으로 전파된다.
- 사용하지 않는 코루틴을 끝낼 수 있는 기능이다.
- Jetpack 통합: 많은 Jetpack 라이브러리에 코루틴을 완전히 지원하는 확장 프로그램이 포함되어 있고, 일부 라이브러리는 자체 코루틴 범위도 제공한다.
- Jetpack에서 코루틴 사용을 밀어주고 있음
코루틴의 구성 요소
- CoroutineScope: 하나 이상의 관련 코루틴을 관리, 모든 코루틴은 해당 범위 내에서 실행해야 한다.
- 종류: CoroutineScope, LifecycleScope, ViewModelScope
- launch: 코루틴을 만들고 함수 본문의 실행을 해당하는 디스패처에 전달하는 함수
- Dispatcher: 코루틴이 어느 스레드에서 실행될지 결정하는 역할
- Dispatchers.Main : UI와 상호작용하고 빠른 작업을 실행하기 위해서만 사용해야 한다.
- Dispatchers.IO : 메인 스레드 외부에서 디스크 또는 네트워크 I/O를 실행하도록 최적화되어 있다.
- Dispatchers.Default : CPU를 많이 사용하는 작업을 실행하는데 최적화되어 있다. (list sort, Json parsing..)
사용 방법
일반적으로 코루틴의 사용법은 다음과 같다.
CoroutineScope(Dispatchers.IO).launch {
...
}
이렇게도 사용할 수 있는데
CoroutineScope.launch {...}
이때의 스레드는 메인스레드로 기본 설정된다.
예제
구글 공식문서의 예제로 살펴보는 코루틴
https://developer.android.com/kotlin/coroutines?hl=ko#executing-in-a-background-thread
Repository.kt
class LoginRepository(private val responseParser: LoginResponseParser) {
private const val loginUrl = "https://example.com/login"
// Function that makes the network request, blocking the current thread
fun makeLoginRequest(
jsonBody: String
): Result<LoginResponse> {
val url = URL(loginUrl)
(url.openConnection() as? HttpURLConnection)?.run {
requestMethod = "POST"
setRequestProperty("Content-Type", "application/json; utf-8")
setRequestProperty("Accept", "application/json")
doOutput = true
outputStream.write(jsonBody.toByteArray())
return Result.Success(responseParser.parse(inputStream))
}
return Result.Error(Exception("Cannot open HttpURLConnection"))
}
}
위의 코드는 일반적인 네트워크 통신 코드이다.
여기서 문제는 해당 코드는 UI 스레드에서 작동한다는 점이다.
따라서 아래와 같은 구조로 변경해주어야 한다.
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
}
}
}
suspend fun
- suspend fun은 일시정지 가능한 함수라는 뜻으로 주로 비동기 함수를 사용할 때 사용된다.
- suspend 함수를 사용하려면 같은 suspend 함수이거나, 코루틴에서 호출해야 한다.
withContext()
- suspend 함수에서 사용할 수 있는 어느 스레드에서 실행할지 설정하는 메서드이다.
- makeLoginRequest 함수는 일시정지가 가능하며, IO 스레드에서 실행되는 함수가 되었다.
ViewModel.kt
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
}
}
}
}
makeLoginRequest 는 suspend 함수로 코루틴을 사용하여 호출해야하기에 코루틴을 사용한 모습이다.
또한 Dispatcher를 설정해주지 않아 UI 스레드에서 작동하도록 하여 데이터를 UI에 적용할 수 있게 만들었다.
요약
- Repository Class에서 makeLoginRequest함수를 통해 IO스레드를 사용하여 네트워크 통신 결과를 리턴한다.
- ViewModel에서 코루틴을 사용하여 UI스레드에서 makeLoginRequest함수의 결과를 받아 작업을 한다.
이는 절대적인 사용방법은 아니며, 응용하여 자유롭게 구현할 수 있다.
'안드로이드 > Asynchronous' 카테고리의 다른 글
[안드로이드] Coroutine Flow (1) | 2024.03.30 |
---|---|
[안드로이드] 스레드 (0) | 2024.03.11 |
[안드로이드] 동기와 비동기 (0) | 2024.03.10 |