이전 글에서는 Kotlin의 Retrofit에 대해서 정리해봤습니다.
안드로이드에서 비동기 작업을 처리할 때 Thread, AsyncTask 등을 사용하면 코드가 복잡해지고 관리하기 어려워집니다. 이를 해결하기 위해 Kotlin에서는 Coroutine 이라는 강력한 비동기 프로그래밍 기법을 제공합니다.
이번 글에서는 코루틴의 개념, 기본 사용법을 알아보겠습니다.
suspend 키워드는 코루틴에서 실행될 수 있는 함수이다.
interface Api {
@GET("public/ticker/ALL_KRW")
suspend fun getCurrentCoinList() : CurrentPriceList
}
suspend 함수는 반드시 코루틴 내부에서 호출해야한다.코루틴을 실행할 때 launch와 async 두 가지 방식이 있다.
launch : 결과를 반환하지 않음 ( 단순 실행 )viewModelScope.launch {
val result = netWorkRepository.getCurrentCoinList()
println("데이터 로드 완료: $result")
}
async : 결과를 반환함 ( Deferred<T> 사용 )viewModelScope.launch {
val result = async { netWorkRepository.getCurrentCoinList() }
println("결과 대기 후 출력: ${result.await()}")
}
class NetWorkRepository {
private val client = RetrofitInstance.getInstance().create(Api::class.java)
suspend fun getCurrentCoinList() = client.getCurrentCoinList()
}
suspend fun을 사용하여 콜백 없이 네트워크 요청 가능viewModelScope.launch로 ViewModel이 살아있는 동안만 실행class SelectViewModel : ViewModel() {
private val netWorkRepository = NetWorkRepository()
private lateinit var currentPriceResultList : ArrayList<CurrentPriceResult>
// 데이터변화를 관찰 LiveData
private val _currentPriceResult = MutableLiveData<List<CurrentPriceResult>>()
val currentPriceResult : LiveData<List<CurrentPriceResult>>
get() = _currentPriceResult
fun getCurrentCoinList() = viewModelScope.launch {
val result = netWorkRepository.getCurrentCoinList()
currentPriceResultList = ArrayList()
for(coin in result.data) {
try{
val gson = Gson()
val gsonToJson = gson.toJson(result.data.get(coin.key))
val gsonFromJson = gson.fromJson(gsonToJson, CurrentPrice::class.java)
val currentPriceResult = CurrentPriceResult(coin.key, gsonFromJson)
currentPriceResultList.add(currentPriceResult)
}catch (e: java.lang.Exception){
Timber.d(e.toString())
}
}
_currentPriceResult.value = currentPriceResultList
}
}
ViewModelScope.launch로 UI가 살아있는 동안만 코루틴 유지try-catch를 이용한 네트워크 에러 처리 가능코루틴은 Dispatcher를 사용해 백그라운드와 메인 스레드를 조정할 수 있다.
launch(Dispatchers.Main) { } // 메인(UI) 스레드 (기본값)
launch(Dispatchers.IO) { } // 네트워크, DB 작업 (백그라운드)
launch(Dispatchers.Default) { } // CPU 집중 작업
-> 네트워크 요청은 IO, UI 업데이트는 Main
viewModelScope.launch(Dispatchers.IO) {
val result = netWorkRepository.getCurrentCoinList()
withContext(Dispatchers.Main) {
_currentPriceResult.value = result // UI 업데이트는 메인 스레드에서!
}
}
-> withContext(Dispatchers.Main)을 사용하면 백그라운드 작업 후 UI 업데이트 가능
suspend fun을 활용해 중단/재계 가능하다.lanunch vs async : 결과 반환 여부에 따라 선택viewModelScope를 사용해 메모리 관리를 최적화한다.