-
Android) Coroutine Exception Handling 어떻게 처리 할까Android 2021. 6. 27. 19:31
예외 처리에는 다양한 방법들이 존재하겠지만, 몇 가지 방법을 알아보려 합니다.
1. try / catch
- 일반적으로 많이 사용하는 try-catch 블록을 사용하는 것입니다.
- try 블록에서 예외를 던질 수 있는 코드를 작성하고, 예외가 발생하면 catch 블록에서 잡힙니다.
- 추가로 finally에서는 예외와 상관없이 해야 하는 코드를 작성해야겠습니다. (메모리 해제 등..)
// ViewModel private val _images = MutableLiveData<ImageResponse>() val images : LiveData<ImageResponse> get() = _images fun fetchImages(query: String?, page: Int, size: Int) = viewModelScope.launch { val result = daumApi.loadImages(query, page, size) result.let { try { if(it.documents.isNotEmpty()) { _images.postValue(it) }else { throw IllegalStateException() } }catch (e:Exception) { // error handling e.printStackTrace() } } }
- 위의 코드는 try-catch 블록을 활용한 코드입니다.
- 사실 try-catch 블록만 사용해도 충분히 예외 처리를 할 수 있다고 생각합니다만, 코드가 많아질수록 boiler plate 코드가 늘어날 수 있고, 관리하기 힘들어 질 수 있습니다.
- 또한, 범위 내에서 자식 중 예외가 발생하면 다음 자식이 실행되지 않고 실행이 종료되게 됩니다.
- 그래서 개별 예외 처리를 다시 한번 해줘야 하는 필요가 생길 수 있습니다.
result.let { try { try { _images.postValue(it) } catch (e:Exception) { // error handling } }catch (e:Exception) { // error handling } }
2. runCatching
- runCatching은 코틀린 1.3 부터 도입된 캡슐화 블록입니다.
- runCatching 블록 안에 성공/실패 여부가 캡슐화된 Result<T> 형태로 리턴합니다.
public inline fun <R> runCatching(block: () -> R): Result<R> { return try { Result.success(block()) } catch (e: Throwable) { Result.failure(e) } }
- 위의 try-catch 블록을 runCatching 블록으로 바꾸면 아래와 같이 표현할 수 있습니다.
fun fetchImages(query: String?, page: Int, size: Int) = viewModelScope.launch { val result = daumApi.loadImages(query, page, size) runCatching { _images.postValue(result) }.onSuccess { // 성공시만 실행 }.onFailure { // 실패시만 실행 (try - catch 블록의 catch와 유사) } }
또한, runCatching의 retrun 값으로 전달받은 Result는 여러 method와 properties를 제공합니다.
runCatching { _images.postValue(result) }.fold({ // 성공시만 실행 },{ // 실패시만 실행 }) var fruitName: String? = null // 실패 시 default 값을 반환 fruitName = fruitResult.getOrDefault("") // 실패 시 else block의 결괏값을 반환 fruitName = fruitResult.getOrElse { when(it) { is IllegalStateException -> "Sold out" is NullPointerException -> "null" else -> throw it } } // 실패시 throwable이 다시 throw fruitName = fruitResult.getOrThrow() // map, recover을 이용해 성공과 실패 시 원하는 값으로 바꿀 수 있음 fruitResult.map { it.toUpperCase() } fruitResult.recover { when(it) { is IllegalStateException -> "Sold out" is NullPointerException -> "null" else -> throw it } }
3. CoroutineExceptionHandler
- CoroutineExceptionHandler는 Thread의 UncaughtExceptionHandler를 구현하는 것으로 작동합니다.
- UncaugthExceptionHandler는 캐치되지 않은 런타임 예외를 처리하는 방법입니다. 즉 Thread에서 캐치되지 않은 런타임 예외를 한 곳에서 처리할 수 있도록 도와줍니다.
- 편하게 예외를 캐치하기 위해 전역으로 CoroutineExceptionHandler를 사용해봅시다.
- coroutineExceptionHandler 블록 안에서 예외 처리를 해줍니다. 코드에서는 SingleLiveEvent를 이용해 예외를 옵저빙 하고 있습니다.
open class BaseViewModel : ViewModel() { private val _isError = SingleLiveEvent<Boolean>() val isError: LiveData<Boolean> get() = _isError protected val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> // error handling throwable.printStackTrace() _isError.call() } }
// ViewModel class KakaoViewModel : BaseViewModel() { init { invokeException() } private fun invokeException() { viewModelScope.launch(coroutineExceptionHandler) { throw IllegalStateException() } } // Activity viewModel.isError.observe(this) { Toast.makeText(this, "Something Went Wrong!", Toast.LENGTH_SHORT).show() }
- 강제로 예외를 발생시키고 있으니, 앱을 실행하자마자 토스트 메시지가 출력되겠죠?
- 3가지의 예외 처리 방법을 알아봤습니다. 물론 정답은 없고, 상황에 맞게 예외를 처리하면 좋을 것 같습니다.
Preference
반응형'Android' 카테고리의 다른 글
Android) 이미지 로딩 라이브러리 자세히 알아보자 (0) 2021.07.11 Android) WebView 정리 (0) 2021.07.08 Android) ImageURL -> Bitmap 으로 변경하기 feat) HttpURLConnection, Coroutines (1) 2021.06.24 Android) Navigation Component IllegalStateException, IllegalArgumentException 예외 (0) 2021.06.10 Android) 안드로이드 고유 식별자 뭘로할까? (6) 2021.06.07