이전의 DB 작업의 연장선이다.
나는 api 통신 후, 해당 결과값을 바탕으로 UI를 갱신하는 것과 DB 갱신을 병렬적으로 진행시키고 싶었다. 그게 더 작업 속도를 빠르게 할 수 있을거라고 생각하기 때문이다.
뭐.. 대충 이런 흐름을 원했다.
그림이 좀 이상하긴 한데ㅋㅋ DB갱신은 별도로 분리하지 않고, ApiCall() 코드의 호출 성공 부분에 함께 작성해 뒀다. (가독성이 좋아서)
아무튼, 성공시 DB를 갱신하는 것과, 서버로 부터 수신한 데이터를 반환하는 코드가 병렬로 수행되도록 하는 것이 목표라는 것이다.
val data = withContext(Dispatcher.IO){ApiCall()}
ApiCall()이 withContext(Dispatcher.IO), I/O 스레드에서 진행이 되기 때문에 사실 아래 처럼 하면 스레드가 하나 더 생성되어 별도로 작업이 수행 될 줄 알았다.
suspend fun ApiCall():String{
runCatching{
...
}.onSuccess{
...
Timber.e("1111")
// 다른 코드
withContext(Dispatcher.IO){
// DB 갱신 코드
Timber.e("2222")
}
Timber.e("3333")
...
}.onFailure{
...
}
Timber.e("4444")
return(...)
}
이걸 작성하면서 나는 로그 표시 순서가 "(1111) -> (3333) -> ( 4444 혹은 2222)" 이럴 것이라고 생각했다.
물론 아니었다.
그냥 다 순차적으로 실행되더라.
어떻게 보면 당연한 결과다. 이미 I/O 스레드에서 실행 중인 코드를 다시 컨텍스트만 바꾸려고 하는 건데 될 턱이 없다.
순전히 내 Coroutine에 대한 이해도가 낮음이 원인인 트러블이다.
이 문제를 통해 처음으로 withContext와 CoroutineScope의 차이를 알게 되었다.
withContext: 이 함수는 코루틴을 현재 컨텍스트에서 다른 컨텍스트로 전환할 때 사용한다.
withContext는 블록의 실행이 완료될 때까지 현재 코루틴의 실행을 일시 중단할 뿐이다.
CoroutineScope: 코루틴을 시작할 수 있는 범위를 제공한다.
CoroutineScope.launch나 CoroutineScope.async를 사용하면 새로운 코루틴을 시작할 수 있으며, 이 코루틴은 해당 스코프의 생명주기에 연결된다.
CoroutineScope는 특정한 스레드나 디스패처에 대해 언급하지 않는 한, 기본적으로 현재 컨텍스트(즉, 호출한 스코프)를 사용하여 코루틴을 실행한다.
withContext(Dispatcher.IO)를 사용할 때 코루틴은 I/O-optimized 스레드 풀에서 실행되며, 작업이 완료되기 전까지는 호출한 코루틴을 블로킹한다.
반면 CoroutineScope.launch는 새 코루틴을 시작하지만 호출자를 블로킹하지 않는다.
즉, withContext는 일종의 스레드 전환과 결과를 기다리는 방식으로 작동하는 반면, launch는 새로운 코루틴을 시작하고 곧바로 제어를 반환하여 호출자가 다른 작업을 계속할 수 있게 한다는 것.
이를 토대로 위 코드의 논리 전개를 다음과 같이 수정했다.
suspend fun ApiCall():String{
runCatching{
...
}.onSuccess{
...
Timber.e("1111")
// 다른 코드
CoroutineScope(Dispatcher.IO).launch{
// DB 갱신 코드
Timber.e("2222")
}
Timber.e("3333")
...
}.onFailure{
...
}
Timber.e("4444")
return(...)
}
이제 얼추 내가 원하는 그림이 나왔다.
근데 4444는 어디갔을까?
runCatching의 onSuccess가 끝나야 호출이 된다.. 이것 조차 당연한 것.
아무튼 해당 부분을 수정하여 코루틴의 작업이 병렬로 처리되도록 할 수 있었다.
Ref.
'Android > Troubleshooting' 카테고리의 다른 글
ROOM DB 마이그레이션 오류 개선 (1) | 2023.12.18 |
---|---|
BuildConfig가 생성 또는 import가 안될 때 (1) | 2023.12.15 |
Haversine 공식으로 두 지점간의 거리를 구하기 (0) | 2023.12.13 |
Data Class의 equals와 hashCode를 사용해 DB 갱신하기 (1) | 2023.11.27 |
ForegroundService 문제 해결하기: ForegroundServiceDidNotStartInTimeException (7) | 2023.10.30 |