Using CoroutineContext to repeat failed HTTP request(번역)
10 Jan 2021 | Coroutine CoroutineContext원본 - Using CoroutineContext to repeat failed HTTP request
안드로이드 앱에서는 인터넷 연결에 문제가 있을 때처럼 무언가 문제가 있을 때면 요청을 다시 보내야 하는 경우가 흔합니다.
여러개의 HTTP 요청을 보내고나서 그 중 실패한 요청들에 한하여 “재요청” 버튼을 누를 수 있도록 하는 화면이 있다고 가정해봅시다(동시에 여러개의 요청을 보내는 상황으로만 생각하실 필요는 없습니다).
만약 화면에 요청이 단 하나만 있다면 매우 간단한 일이겠죠. 단지 그 요청을 다시 요청하면 되니까요. 그러나 한 화면에서 제각각 다른 시각에 보내진 여러 다른 요청들이 있다면, 우리는 그 요청들을 구분해낼 수 있는 방법이 필요합니다. 마지막에 호출된 함수 자체를 계속 저장해놓는 방법이 있긴 하지만 이건 유연하지 못할 겁니다. 만약 몇몇 요청은 “다시 시도하기” 다이얼로그를 띄워주지 않아도 될 경우 그 변수들을 다 지워줘야 하고, 만약 지워주는 걸 까먹는다면 “다시 시도하기” 버튼을 눌렀을 경우 성공한 함수가 재요청되는 버그가 되겠죠.
간단한 해결법은 CoroutineContext를 사용하여 나중에 다시 시도해야하는 함수를 저장하는 것입니다.
기본적으로 CoroutineContext는 Job
, CoroutineDispatcher
, CoroutineName
, CoroutineExceptionHandler
들로 구성됩니다(자세한 내용는 article about Coroutines에서 확인할 수 있습니다). 그러나 공식 문서의 정의에 따르면 CoroutineContext는 그저 Element
객체들의 indexed set입니다. 이말은 즉 AbstractCoroutineContextElement
클래스를 확장하여 자신만의 Element
interface를 구현할 수도 있다는 이야기입니다.
class RetryCallback(val callback: () -> Unit) :
AbstractCoroutineContextElement(RetryCallback) {
companion object Key : CoroutineContext.Key<RetryCallback>
}
위와 같이 코드를 작성하고나면 우리가 시작한 코루틴의 CoroutineContext에 RetryCallback를 추가할 수 있습니다(launch의 첫 번째 인자로 CoroutineContext를 넘깁니다).
fun someFunction(someParam: List<String>) {
viewModelScope.launch(exceptionHandler
+ RetryCallback { someFunction(someParam) }) {
// perform the request, update the view with the result
}
}
그리고 만약 request가 실패한다면 CoroutineExceptionHandler에서 callback을 다룰수 있습니다.
private val exceptionHandler = CoroutineExceptionHandler {
coroutineContext, throwable ->
val callback: (() -> Unit)? = coroutineContext[RetryCallback.Key]
?.callback
// ...
}
CoroutineExceptionHandler에서 단순히 함수를 Activity나 Fragment로 넘기고, 사용자가 “다시 시도하기”버튼을 클릭할 때 그 함수를 실행히시키만 하면 됩니다.
Comments