비정상 종료 감지 및 진단

처리되지 않은 예외나 신호로 인해 예상치 못한 종료가 발생할 때마다 Android 앱이 비정상 종료됩니다. 자바를 사용하여 작성된 앱은 Throwable 클래스로 표시되는 처리되지 않은 예외가 발생하면 비정상 종료됩니다. 네이티브 코드 언어를 사용하여 작성된 앱은 실행 중에 SIGSEGV와 같은 처리되지 않은 신호가 있는 경우 비정상 종료됩니다.

앱이 비정상 종료되면 Android는 앱 프로세스를 종료하고 대화상자를 표시하여 그림 1과 같이 앱이 중지되었음을 사용자에게 알립니다.

Android 기기에서 앱 비정상 종료

그림 1. Android 기기에서 앱 비정상 종료

앱이 포그라운드에서 실행되고 있어야만 비정상 종료되는 것은 아닙니다. 백그라운드에서 실행 중인 broadcast receiver나 콘텐츠 제공업체까지 포함하여 모든 앱 구성요소가 앱의 비정상 종료를 유발할 수 있습니다. 이러한 비정상 종료는 사용자에게 혼란스러울 때가 많은데 적극적으로 사용자가 앱에 참여하지 않기 때문입니다.

앱에 비정상 종료가 발생하면 이 페이지의 안내를 사용하여 문제를 진단하고 해결할 수 있습니다. 네이티브 코드 언어를 사용하여 빌드된 앱의 비정상 종료를 진단하는 방법에 관한 안내는 네이티브 충돌 진단하기를 참조하세요.

문제 감지

사용자가 앱에서 비정상 종료를 많이 경험한다는 것을 항상 알 수 있는 것은 아닙니다. 앱을 이미 게시했다면 Android vitals를 사용하여 문제를 인식할 수 있습니다.

Android vitals

Android vitals를 사용하면 앱이 과도한 비정상 종료를 보이는 경우 Play Console을 통해 알림을 보냄으로써 앱 성능을 개선할 수 있습니다. Android vitals는 앱이 다음과 같을 때 비정상 종료가 과도하다고 간주합니다.

  • 일일 세션의 1.09% 이상에서 한 번 이상의 비정상 종료가 발생합니다.
  • 일일 세션의 0.18% 이상에서 두 번 이상의 비정상 종료가 발생합니다.

일일 세션이란 앱이 사용된 1일을 의미합니다. Google Play에서 Android vitals 데이터를 수집하는 방법에 관한 자세한 내용은 Play Console 문서를 참조하세요.

앱에 지나치게 많은 비정상 종료가 발생하는 것을 알았다면 다음 단계는 이를 진단하는 것입니다.

비정상 종료 진단

비정상 종료 문제를 해결하는 일은 어려울 수 있습니다. 그러나 비정상 종료의 근본 원인을 식별할 수 있다면 쉽게 해결책을 찾을 수 있습니다.

앱에 비정상 종료를 일으킬 수 있는 상황은 여러 가지가 있습니다. null 값이나 빈 문자열을 확인하는 것처럼 분명한 이유도 있지만 API에 무효한 인수 전달이나 복잡한 멀티스레드 상호작용처럼 불분명한 이유가 대부분입니다.

스택 추적 읽기

비정상 종료 문제를 해결하는 첫 단계는 발생한 장소를 식별하는 것입니다. Play Console이나 logcat 도구 출력을 사용하고 있다면 보고서 세부정보에 있는 스택 추적을 사용할 수 있습니다. 사용할 수 있는 스택 추적이 없다면 수동으로 앱을 테스트하거나 영향을 받는 사용자에게 연락하여 비정상 종료를 로컬로 재현하고 logcat을 사용하는 동안 재현해야 합니다.

다음 추적은 샘플 앱에서 발생한 비정상 종료의 예를 보여줍니다.

--------- beginning of crash
    AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.developer.crashsample, PID: 3686
    java.lang.NullPointerException: crash sample
    at com.android.developer.crashsample.MainActivity$1.onClick(MainActivity.java:27)
    at android.view.View.performClick(View.java:6134)
    at android.view.View$PerformClick.run(View.java:23965)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:156)
    at android.app.ActivityThread.main(ActivityThread.java:6440)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:746)
    --------- beginning of system
    

스택 추적은 비정상 종료를 디버깅하는 데 중요한 정보 두 가지를 표시합니다.

  • 발생한 예외 유형
  • 예외가 발생한 코드 섹션

발생한 예외 유형은 일반적으로 무엇이 잘못되었는지에 관한 매우 강력한 힌트입니다. 유형이 IOException, OutOfMemoryError 또는 그 외의 것인지 살펴보고 예외 클래스에 관한 문서를 찾습니다.

예외가 발생한 소스 파일의 줄 번호인 클래스, 메서드, 파일이 스택 추적의 두 번째 줄에 표시됩니다. 호출된 각 함수의 경우 다른 줄에서 이전의 호출 사이트(스택 프레임이라고 함)를 표시합니다. 스택을 올라가며 코드를 검사하면 잘못된 값을 전달하는 장소를 찾을 수 있습니다. 코드가 스택 추적에 표시되지 않으면 무효한 매개변수를 비동기 작업에 전달한 어딘가일 수 있습니다. 스택 추적의 각 줄을 검사하고 사용한 API 클래스를 찾으며 전달한 매개변수가 올바른지, 허용된 장소에서 호출했는지를 확인하여 어떤 일이 발생했는지 파악하는 경우가 많습니다.

네이티브 앱에서 발생하는 비정상 종료에 관한 자세한 내용은 네이티브 충돌 진단하기를 참조하세요.

비정상 종료 재현을 위한 도움말

에뮬레이터를 시작하거나 기기를 컴퓨터에 연결하는 것만으로 문제를 재현할 수는 없습니다. 개발 환경에는 대역폭, 메모리, 저장소와 같은 많은 리소스가 있습니다. 예외 유형을 사용하여 부족한 리소스가 무엇인지 확인하거나 Android 버전, 기기 유형 또는 앱 버전 사이의 상관관계를 찾으세요.

메모리 오류

OutOfMemoryError가 있는 경우 메모리 용량이 낮은 에뮬레이터를 만들어 시작할 수 있습니다. 그림 2는 기기의 메모리양을 제어할 수 있는 AVD manager 설정을 보여줍니다.

AVD Manager에서 메모리 설정

그림 2. AVD Manager에서 메모리 설정

네트워킹 예외

사용자는 모바일 또는 Wi-Fi 네트워크 적용 범위 내외로 자주 이동하기 때문에 애플리케이션 네트워크 예외에서는 일반적으로 오류가 아니라 예기치 않게 발생하는 정상적인 작동 조건으로 처리되어야 합니다.

UnknownHostException과 같은 네트워크 예외를 재현해야 한다면 애플리케이션이 네트워크를 사용하려고 하는 동안 비행기 모드를 켜보세요.

또 다른 옵션은 네트워크 속도 에뮬레이션 또는 네트워크 지연을 선택하여 에뮬레이터의 네트워크 품질을 낮추는 것입니다. AVD Manager에서 SpeedLatency 설정을 사용하거나 다음 명령줄 예에서 보듯이 -netdelay-netspeed 플래그를 사용하여 에뮬레이터를 시작할 수 있습니다.

emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm
    

이 예에서는 모든 네트워크 요청에서 20초 지연 시간과 업로드 및 다운로드 속도 14.4Kbps를 설정합니다. 에뮬레이터의 명령줄 옵션에 관한 자세한 내용은 명령줄에서 에뮬레이터 시작을 참조하세요.

logcat으로 읽기

비정상 종료를 재현하는 단계를 실행하고 나면 logcat과 같은 도구를 사용하여 자세한 정보를 얻을 수 있습니다.

logcat 출력에서는 인쇄한 다른 로그 메시지가 무엇인지를 시스템의 기타 사항과 함께 표시합니다. 추가한 Log 구문을 인쇄하면 앱이 실행되는 동안 CPU와 배터리가 낭비되므로 끄는 것을 잊지 마세요.