비정상종료 예외처리를 효율적으로 해보자

지프치프·2023년 11월 3일
0

Android

목록 보기
79/85

개요

이전에 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를 출력하는 걸 볼 수 있다.

갈무리

위 예제에서 더 나아간다면 로그를 파일로 작성한 뒤
작성된 로그 파일을 개발자에게 보내주는 방법도 있겠다.

우리가 프로그램을 사용하다가 비정상종료 되었을 때
재실행하면 나타나는 개발자한테 보내는 버그 레포트가 이에 해당되는 것 같다.

profile
지프처럼 거침없는 개발을 하고싶은 개발자

0개의 댓글