안드로이드 애플리케이션을 개발하다보면 OOM 등과 같은 사유로 인해 앱에 크래쉬가 발생하여 비정상 종료된 경험이 존재할 것이다.
일반적으로 이러한 크래쉬가 발생하면 로그를 확인하려고 하겠지만, 비정상 종료같은 경우 onPause, onDestroy 같은 상황에서는 로직이 정상적으로 진행되지 않을 수 있다.
이러한 이유로 많은 이들이 FirebaseCrashLytics 를 활용하여 로그를 수집하고, 크래쉬 원인을 분석하는데 이것만이 아닌 다른 이벤트도 처리하고 싶을 수 있다.
이 경우 setDefaultUncaughtExceptionHandler 를 활용하면 된다.
Thread class 에 존재하는 함수로 이름처럼 Catch 되지 않은 Exception 이 발생하는 경우 이를 핸들링하는 함수이다.
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
// 한 번만 설정하기 위해 Application 단에서 설정한다.
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
// 여기서 서버로 로그 파일을 보내던가, 특정 Activity 를 띄워주는 등의 행동이 가능하다.
API.sendLog(throwable)
}
}
}
Thread.setDefaultUncaughtExceptionHandler 의 경우 마지막에 설정한 값으로 Handler 로 덮어씌워지기 때문에 기존 Handler 는 호출되지 않는다.
만약 위처럼 커스텀하는 경우 올바른 종료 코드를 넣지 않는다면 에러가 발생하였을 때 예상치 못한 버그가 발생할 수 있다. 가장 추천하는 것은 기존 handler 를 다시 넣어주는 것이다.
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
// 한 번만 설정하기 위해 Application 단에서 설정한다.
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
// 여기서 서버로 로그 파일을 보내던가, 특정 Activity 를 띄워주는 등의 행동이 가능하다.
API.sendLog(throwable)
/*
모든 동작을 처리하였으면 기존 exceptionHandler 를 실행시켜주자.
진행하지 않으면 예상치 못한 버그가 발생할 수 있다.
*/
defaultHandler?.uncaughtException(thread, throwable)
}
}
}
FirebaseCrashLytics 도 내부적으로는 Thread.setDefaultUncaughtExceptionHandler 를 구현하여 Firebase Server 로 로그를 전송한다.
CrashLyticsController.java 내의 코드
그렇기에 커스텀 로직을 처리한 이후에 FirebaseCrashLytics 를 등록하면 기존 로직이 호출되지 않는다.
만약 FirebaseCrashLytics 와 함께 사용하고 싶은 경우 먼저 FirebaseCrashLytics 를 등록하고 커스텀 로직 설정 후 내부에서 defaultHandler 를 호출해주자.
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
// FirebaseCrashLytics 등록 ..
FirebaseCrashLytics...
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
// 한 번만 설정하기 위해 Application 단에서 설정한다.
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
// 여기서 서버로 로그 파일을 보내던가, 특정 Activity 를 띄워주는 등의 행동이 가능하다.
API.sendLog(throwable)
/*
모든 동작을 처리하였으면 기존 exceptionHandler 를 실행시켜주자.
진행하지 않으면 예상치 못한 버그가 발생할 수 있다.
*/
defaultHandler?.uncaughtException(thread, throwable)
}
}
}