안드로이드 앱 개발을 할 때 retrofit을 많이 쓰시죠.
그때 Coroutine과 같이 사용을 할 때 Dispatcher를 설정해주고 있으신가요?
그렇다면 이제부터라도 안쓰시는 게 낫다.
Retrofit에서 interface를 정의하는 방식은 총 3가지가 있다.
이 메서드는 동기적으로 요청을 한다.
요청을 한 순간 해당 스레드가 막히고 따로 쓰레드를 지정안해주면
ANR이 발생할 수 있다.
이 메서드는 서버에 비동기적으로 요청을 하고 콜백을 통해 반환값을 받는다.
콜백을 통해 코드를 작성하면 가독성과 코드 양이 많아지므로 Coroutine과 같이 쓰는 걸 추천한다.
// KotlinExtensions.kt
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val service = invocation.service()
val method = invocation.method()
val e = KotlinNullPointerException(
"Response from ${service.name}.${method.name}" +
" was null but response body type was declared as non-null",
)
continuation.resumeWithException(e)
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
위에 코드를 보면 suspendCancellableCoroutine으로 블럭을 만들어서 enqueue를 이용하여 내부적으로 콜백을 구현한 걸 볼 수 있다. 그래서 별도에
enqueue()를 사용할 필요없이 호출만 해주면 내부적으로 처리해주기 때문에 가독성 측면에서 매우 좋다. 하지만 콜백을 직접 오버라이드 하는 게 아니여서 성공인지 실패인지는 직접 확인하여 처리해줘야된다.
왜 안써도 되는 것인지 이제 본격적으로 알아보자.
CallFactory는 네트워크를 담당하는 OkHttpClient의 인터페이스다.
Dispatcher.kt에 있는 코드이다. 실제로 요청을 할 때 별도의 ThreadPool을 생성하여 동작시키고 있는 걸 확인할 수 있었다.
이걸 갑자기 왜 보여주냐면 enqueue을 실행하면 내부적으로 해당 dispatcher를 이용하여 네트워크 요청을 하게된다.!
enqueue내부 코드이다. promoteAndExecute()를 살펴보면
요약하자면 대기중인 요청을 검사하여 실행 가능하면 실행 리스트로 넘긴 후
스레드풀에서 동작하게 하는 로직이다. 여기서 executorService라는 스레드풀로 넘기는데
이게 바로 아까 봤던 별도로 생성된 스레드풀이다.
그래서 결론적으로는 io로 안해줘도 별도의 스레드풀을 생성하여 동작한다
네트워크 통신이 끝난 후 콜백을 어느 스레드에서 받을 지 결정한다.
만약 아무 설정을 안해주면 Platform.callbackExecutor로 초기화가 되는데
안드로이드 개발이라면 Dalvik으로 가서 AndroidMainExecutor()로 생성이 될 것이다.
마지막으로 AndroidExecutor로 들어가보면 MainLooper에 가져와서 처리해주고 있는 걸 볼 수 있다.
이렇게 내부코드를 통해 말로만 듣던 어떻게 io에서 처리가 되고 콜백은 main에서 처리가 되는 지 눈으로 확인해보니깐 깊게 이해한 느낌이 들어 뿌듯하다.
앞으로 뭔가 궁금증이 생길때는 내부코드를 보는 버릇을 들여야 될 것 같다.