RxJava, Reactor, 멀티 스레드, 콜백함수 등 이미 다양한 비동기적 연산을 위한 방법이 있는데 코틀린 코루틴을 배워야할까 🤔
⚠️ 메인 스레드 블로킹 문제
fun onCreate() {
val news = getNewsFromApi()
val sortedNews = news.sortedByDescending { it.publishedAt }
view.showNews(sortedNews)
}
onCreate 함수가 메인 스레드에서 실행 → getNewsFromApi 함수가 스레드를 블로킹 → 앱 크래시 💥getNewsFromApi 함수를 다른 스레드에서 실행 → showNews 호출시 정보가 없어 메인스레드에서도 마찬가지로 크래시 💥fun onCreate() {
val news = getNewsFromApi()
val sortedNews = news.sortedByDescending { it.publishedAt }
runOnUiThread {
view.showNews(sortedNews)
}
}
fun onCreate() {
getNewsFromApi { news ->
val sortedNews = news.sortedByDescending { it.publishedAt }
view.showNews(sortedNews)
}fun showNews() {
getConfigFromApi { config ->
getNewsFromApi(config) { news ->
getUserFromApi { user ->
view.showNews(user, news)
}
}
}
}// As-Is (Not-Working)
fun onCreate() {
showProgressBar()
showNews()
hideProgressBar() // wrong point
}// To-Be
fun onCreate() {
showProgressBar()
showNews() {
hideProgressBar()
}
}RxJava, Reactor와 같은 리액티브 스트림을 사용하면 데이터 스트림 내에서 일어나는 모든 연산을 시작, 처리, 관찰할 수 있음
리액티브 스트림은 스레드 전환과 동시성 처리를 지원 → 애플리케이션 내의 연산을 병렬 처리하는데 사용됨
fun onCreate() {
disposables += getNewsFromApi() {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { news ->
news.sortedByDescending { it.publishedAt }
}
.subscribe { sortedNews ->
view.showNews(sortedNews)
}
}
위 예제에서 disposables는 사용자가 스크린을 빠져나갈 경우 스트림을 취소하기 위해 필요
RxJava를 사용한 방법이 콜백을 사용한 것보다 훨씬 더 좋은 방법
하지만 단점도 존재
subscribeOn, observeOn, map, subscribe와 같은 함수들을 배워야 함 📘Observable 이나 Single클래스로 래핑해야 함fun getNewsFromApi(): Single<List<News>>
fun showNews() {
disposables += Observable.zip(
getConfigFromApi().flatMap { getNewsFromApi(it) },
getUserFromApi(),
Function2 { news: List<News>, config: Config ->
Pair(news, config)
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { (news, config) ->
view.showNews(news, config)
}
}
val scope = CoroutineScope(Dispatchers.Main)
fun onCreate() {
scope.launch { updateNews() }
scope.launch { updateProfile() }
}
suspend fun updateNews() {
showProgressBar()
val news = getNewsFromApi()
val sortedNews = news.sortedByDescending { it.publishedAt }
view.showNews(sortedNews)
hideProgressBar()
}
suspend fun updateProfile() {
val user = getUserData()
view.showUser(user)
}
updateNews 함수가 네트워크 응답을 기다리는 동안, 메인 스레드는 updateProfile 함수가 사용getUserData 호출은 사용자의 데이터가 캐싱되어 있기 때문에 중단되지 않는다고 가정updateProfile은 메인 스레드 내에서 멈추지 않고 실행됨updateNews를 처리하는 코루틴에 의해 getNewsFromApi 이후 작업을 수행코루틴은 중단했다가 다시 실행할 수 있는 컴포넌트(재사용 가능한 독립된 모듈)라고 할 수 있다.
fun onCreate() {
viewModelScope.launch {
val news = getNewsFromApi()
val sortedNews = news.sortedByDescending { it.publishedAt }
view.showNews(sortedNews)
}
}
viewModelScope는 현재 안드로이드에서 가장 흔하게 사용하는 스코프. viewModelScope 대신 사용할 스코프를 지정할 수 있음.
fun showNews() {
viewModelScope.launch {
val config = getConfigFromApi()
val news = getNewsFromApi(config)
val user = getUserFromApi()
view.showNews(user, news)
}
}
async 사용 (요청을 처리하기 위해 만들어진 코루틴을 즉시 시작하는 함수로, await 과 같은 함수를 호출하여 결과를 기다림fun showNews() {
viewModelScope.launch {
val config = async { getConfigFromApi() }
val news = async { getNewsFromApi(config.await()) }
val user = async { getUserFromApi() }
view.showNews(user.await(), news.await())
}
}
suspend 제어자(modifier)를 추가하면 됨코루틴을 사용하는 가장 중요한 이유는 스레드를 사용하는 비용이 크기 때문. 스레드는 명시적으로 생성해야 하고, 유지되어야 하며, 스레드를 위한 메모리 또한 할당되어야 함.
코틀린 코루틴은 동시성 프로그래밍을 최대한 쉽게 구현할 수 있도록 도와줌
1. 코루틴의 배경과 중요성
2. 안드로이드에서의 비동기 처리 방법
3. 코드 비교
async와 await를 활용해 작업을 병렬로 처리 가능.4. 백엔드에서의 활용
suspend 키워드만 추가하면 비동기 작업을 쉽게 전환 가능.5. 결론