이전에 UncaughtExceptionHandler
를 이용하여
비정상종료 예외처리에 대해서 포스팅을 한 적이 있었다.
당시 포스팅에선 발생한 Exception에 대해 Log만 출력하고 앱을 종료하는 예제를 작성했는데 이렇게 되면 UX면에서 부자연스럽다는 생각이 들어 개선을 해보았다.
비정상종료가 발생하면 앱을 재시작한 뒤 Dialog로 적절한 메세지를 띄워주는 예제를 작성해보도록 하겠다.
먼저 Application 클래스를 생성하여 ExceptionHandler를 설정한다.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler { t, e ->
Log.e("Sample", Log.getStackTraceString(e))
// Restart the app
startActivity(Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
putExtra("uncaughtException", e)
})
exitProcess(0)
}
}
}
setDefaultUncaughtExceptionHandler
를 통해 콜백이 들어오면
발생한 Exception에 대해 Log를 출력한 뒤 앱을 재실행하도록 하였다.
앱을 재실행 할 때 Dialog를 띄워줄 수 있도록 Throwable
객체를 액티비티에 넘겨준다.
앱을 재실행 한 뒤에는 반드시 아래와 같은 메소드를 호출하여 프로세스를 중단해야한다.
exitProcess()
android.os.Process.killProcess(android.os.Process.myPid())
그렇지 않으면 setDefaultUncaughtExceptionHandler
에 의해 Pending 상태가 되어있는 기존 프로세스가 종료되지 않아 ANR이 발생한다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
intent.getSerializableExtra("uncaughtException")?.let {
val exception = it as? Throwable
AlertDialog.Builder(this)
.setMessage("""
비정상종료가 탐지되었습니다!
Exception >> ${exception.toString()}
""".trimIndent())
.setCancelable(false)
.setPositiveButton("확인", null)
.show()
}
// init UI
binding.apply {
btnException.setOnClickListener {
throw RuntimeException("Exception for test!")
}
}
}
}
버튼을 누르면 RuntimeException
을 던져서 강제로 예외를 발생시키게 하였다.
intent
를 통해 넘어온 Throwable
객체를 가지고
Dialog에 적절한 메세지를 적어 출력하면 된다.
실행해보면 아래 캡처와 같이 Dialog를 출력하는 걸 볼 수 있다.
위 예제에서 더 나아간다면 로그를 파일로 작성한 뒤
작성된 로그 파일을 개발자에게 보내주는 방법도 있겠다.
우리가 프로그램을 사용하다가 비정상종료 되었을 때
재실행하면 나타나는 개발자한테 보내는 버그 레포트가 이에 해당되는 것 같다.