핀수로그
  • [Android] 전역적인 예외처리하기 | Thread.setDefaultUncaughtExceptionHandler
    2022년 11월 22일 16시 09분 32초에 업로드 된 글입니다.
    작성자: 핀수
    728x90
    반응형

    상황

    앱 사용 중 앱이 픽 죽어버리거나 응답 없음 창(ANR)이 뜨는 것을 경험한 적 있을 것이다. 한두번이야 왜이래~~ 하면서 다시 앱을 실행해 사용하겠지만 이러한 상황이 지속된다면?

    끔찍한 사용자 경험을 제공하게 되고 사용자들은 더 이상 해당 앱을 찾지 않을 것이다.

    개발을 하다보면 여러가지 오류와 만나게 된다. 예상 가능한 범위의 오류는 처리할 수 있다지만 그렇지 못한 경우에는 어떻게 해야할까?

    오늘은 전역적인 예외 처리에 대해 알아보려고 한다.

    비정상 종료

    가장 최근 업데이트한 버전 1.1.0에서 비정상 종료가 발생한 것을 확인했다.

    경험으로 미루어보아 앱이 냅다 꺼져버린 거겠지

    해당 원인을 찾아 해결하는 것도 중요하지만 이러한 상황이 발생했을 때 앱이 죽도록 내버려두는 것이 아니라 이를 사용자에게 적절하게 안내하고 대응할 수 있도록 하는 게 사용자 경험을 잃지 않는 좋은 방법이라고 생각한다.

    전역적인 예외처리

    해당 처리는 Application를 상속받은 PairPlayApplication.kt 에서 처리할 것이다.

    class PairPlayApplication : Application() {
    
        override fun onCreate() {
            super.onCreate()
    
            Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() }
        }
    
        private fun caughtException(){
            // start error activity
            startErrorActivity()
            // kill process
            Process.killProcess(Process.myPid())
            exitProcess(-1)
        }
    
        private fun startErrorActivity(){
            val intent = Intent(this, ErrorActivity::class.java)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
            startActivity(intent)
        }
    }

    Application

    👉🏻 전역 애플리케이션 상태를 유지하기 위한 기본 클래스

    👉🏻 해당 클래스 또는 해당 클래스의 하위 클래스는 응용 프로그램/패키지에 대한 프로세스가 생성될 때 다른 클래스보다 먼저 인스턴스화 된다.

    하위 클래스를 생성한 경우 AndroidManifest.xml 태그 내의 name 속성에 해당 클래스를 지정해주면 된다.

    <application
            android:name=".util.PairPlayApplication"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.PairPlay">
            ...
    </application>

    onCreate()

    Activity, service 또는 receiver 가 실행되는 시점보다 먼저 호출된다. (content provider는 제외)

    이 앱이 실행되기 시작하는 시점에 해당 메소드가 호출 된다.

    Thread.setDefaultUncaughtExceptionHandler

    이름 그대로 잡히지 않은 예외에 대한 기본 핸들러를 지정하는 것이다.

    우리가 처리하지 않은 부분에서 예외가 발생할 경우 해당 메소드에서 처리를 한다.

    나의 경우 해당 프로세스를 종료하고 ErrorActivity를 통해 사용자에게 오류가 발생했음을 알린다.

    임의로 기기검색 버튼을 클릭하면 RuntimeException이 발생하도록 설정했다.

    부록

    지금 앱 같은 경우 워낙 단순한 앱이라 그냥 재시작하는 정도로만 처리했지만

    복잡한 앱 같은 경우에는 단순히 재시작을 하면 사용성이 그렇게 좋진 않을 것이다.

    예외가 발생한 그 시점으로 돌아가는 것이 바람직하다고 생각한다.

    SimpleActivityLifecycleCallbacks 을 사용하면 오류가 발생했던 Activity로 돌아가는 것이 가능하다.

    만약 SAA구조라 해당 fragment로 돌아가야한다면?

    Jetpack Navigation을 사용하는 경우 해당 처리가 가능하다.

    세션 또는 토큰 동기화 문제 또한 생각해봐야 할 것이다.

    필자의 경우 쿠키를 이용한 세션 유지 방식을 사용하고 있었는데 CookieJar를 통해 해당 쿠키를 유지할 수 있었다.

    다음에 기회가 된다면 자세한 내용을 작성해보는 것도 좋을 것이다.


    References

    아래 글을 참고하여 작성 되었습니다.

    Application | Android Developers

    Thread.UncaughtExceptionHandler | Android Developers

    728x90
    반응형
    댓글