작업을 중단 했다가 다시 실행할 수 있는 컴포넌트로, 비동기 작업을 쉽게 관리할 수 있다.
안드로이드에서는 하나의 앱에서 뷰를 다루는 스레드가 단 하나만 존재하기 때문에 Main 스레드는 블로킹되지 않는 것이 중요하다.
따라서, 안드로이드 UI 작업은 MainThread에서만 실행되야 한다. 메인 스레드가 블록되면 ANR (Application Not Responding) 오류가 발생할 수 있다.
안드로이드 앱은 일반적으로 초당 60 프레임의 랜더링을 목표로 하고 1 프레임을 1/60초 (약 16ms) 마다 그려야 한다.
CoroutineScope는 코루틴을 실행할 수 있는 환경을 제공CoroutineContext는 코루틴이 실행되는 컨텍스트(예: 디스패처, Job 등)를 정의CoroutineContext를 통해 CoroutineScope를 만들고, 이를 사용해 코루틴을 실행launch → Job 객체이며, 결과값을 반환 Xasync → Deffered 객체이며, 결과값을 반환withContextCoroutineContext(Dispatchers.IO 등)로 코루틴의 실행 컨텍스트를 변경하고, 해당 컨텍스트에서 주어진 suspend 함수를 실행하는 함수suspend 함수가 완료된 후 결과를 반환withContext 호출 전의 컨텍스트로 다시 돌아옴Activity, Fragment, Service 등)의 생명주기에 연동된 코루틴 스코프repeatOnLifeCycler (거의 주로 사용함)launchWhenStarted
lifecycleScope.launch {
// 1. API 호출 - 백그라운드 스레드에서 수행
val apiResponse = withContext(Dispatchers.IO) {
apiService.getSomeData() // 이 함수는 suspend 함수
}
// 2. UI 업데이트 - 메인 스레드에서 수행
withContext(Dispatchers.Main) {
// DTO에서 값을 꺼내서 XML의 뷰에 바인딩
textView.text = apiResponse.someProperty
imageView.setImageResource(apiResponse.imageResource)
}
}
private fun observeViewModel() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
customerViewModel.getMemberProfile(memberId)
customerViewModel.memberProfileResponse.collect() { res ->
binding.managementPageMember = res.body()
val membership = res.body()?.membership
if (membership != null) { checkMembershipRadioButton(membership) }
}
}
}
}
ViewModel 및 lifecycle owner(Activity 또는 Fragment) 이외에 Coroutine을사용시
CoroutineScope를 사용
예를 들어, 클래스의 수명주기가 명확하지 않거나 코루틴의 수명을 명시적으로 관리하고 싶을 때
CoroutineScope를 직접 정의하여 사용
ViewModel 에서 비동기 네트워크 통신시 사용되는 CoroutineScope
ViewModel과 생명주기 공유
뷰모델 제거되면(onCleared) 모든 코루틴 작업 자동 취소됨
Dispatchers.Main이 기본CoroutineContext로 설정되있음
private val _spotPositionResponse = MutableStateFlow(Response.success(SpotPositionResponse()))
val spotPositionResponse: StateFlow<Response<SpotPositionResponse>> = _spotPositionResponse
fun getSpotList(regionId: Int) {
viewModelScope.launch {
try {
mainApiRepository.getSpotList(regionId).collect {
_spotPositionResponse.value = it
}
} catch (e:Exception) {
Log.e("ViewModel getSpotList Error", e.message.toString())
}
}
}
CoroutineScope를 제공하지만 block 기능이 있는 scope 빌더runBlocking 내부의 코드가 실행될 때까지 기다림이유는 Retrofit 라이브러리는 네트워크 요청을 실행하는 동안 내부적으로 자체적인 비동기 처리를 수행하기 때문이다.
Coroutine을 이용할 경우 내부적으로 enqueue를 진행해주고 있으며, enqueue는 retrofit에서 내부적으로 비동기 처리를 해주고 있으니 Dispatchers.IO를 사용하지 않아도 된다.
Room DAO 쿼리 메서드 suspend 사용하면 자동으로 Dispatchers.IO 수행한다.