PhoneGap/Cordova Android 앱을 강제로 크래시하게 만드는 방법
1. 문제 정의
크래시 리포터(Crash Reporter) 플러그인을 개발 중인데, 테스트를 위해 앱을 의도적으로 크래시시키고 “Unfortunately, application has stopped” 창(앱 중단 다이얼로그)을 띄우고 싶은 상황입니다.
그런데 자바스크립트에서 의도적으로 unhandled exception을 발생시켜도 앱이 크래시하지 않습니다. 오히려 같은 화면이 그대로 유지됩니다.
| |
심지어 자바스크립트에서 1시간 동안 무한 루프를 돌려도 앱은 크래시하지 않고 그대로 멈춰 있었습니다.
핵심 증상 정리
| 시도 | 결과 |
|---|---|
JS에서 throw new Error() 발생 | 앱이 크래시하지 않음, 같은 화면 유지 |
| JS에서 무한 루프 실행 (1시간) | 앱이 크래시하지 않음, 응답 불능 상태만 |
네이티브 Java에서 10 / 0 예외 발생 | ??? (방법에 따라 크래시됨) |
2. 원인 탐구
왜 자바스크립트 예외로는 앱이 크래시하지 않을까요? 이는 Cordova의 아키텍처 때문입니다.
PhoneGap/Cordova 앱은 자바스크립트와 네이티브 코드(Java)가 WebView를 사이에 두고 통신합니다. 자바스크립트에서 예외가 발생해도, 이 예외는 자바스크립트 런타임(WebView 엔진) 레벨에서 소비되며 네이티브 코드(Java) 계층으로 전파되지 않습니다.
즉, 자바스크립트의 unhandled exception은 네이티브 안드로이드 런타임의 “uncaught exception"으로 변환되지 않으므로, 안드로이드 프로세스가 죽지 않는 것입니다. 앱이 크래시하려면 네이티브(Java) 레벨에서 처리되지 않은 예외가 발생해야 합니다.
3. 근본 원인 분석
문제의 본질은 다음 두 가지입니다.
첫째, 자바스크립트 예외는 네이티브 계층에 도달하지 못합니다. 크래시 리포트 플러그인이 잡으려는 “애플리케이션 크래시"는 네이티브 uncaught exception으로 정의됩니다. JS 계층의 예외는 이것과 별개의 사건입니다.
둘째, Cordova 내부에서 예외를 삼키는 catch 블록이 존재합니다. 네이티브 플러그인 내부에서 예외를 던져도, Cordova 프레임워크가 이를 감싸고 있는 catch 계층이 예외를 처리해버리기 때문에 앱이 크래시하지 않습니다. org.apache.cordova 패키지 안의 다음 두 클래스가 핵심입니다.
PluginManager클래스의execHelper메서드의catch (Exception e) {}블록ExposedJsApi클래스의exec메서드의catch (Throwable e) {}블록
이 catch 블록들이 예외를 받아 콘솔 로그만 남기고 삼켜버리므로, 부모 계층까지 exception이 전파되지 않아 크래시로 이어지지 않습니다.
4. 코드 해결책
크래시를 유도하는 방법은 두 가지가 있습니다. 목적(메뉴 버튼을 누를 때 vs 자바스크립트 호출로)에 따라 선택합니다.
방법 A: 메뉴 버튼 누를 때 크래시 (CordovaActivity 수정)
android/src/<package>/CordovaActivity.java 의 onCreateOptionsMenu 를 다음과 같이 수정합니다.
| |
그런 다음 앱에서 메뉴 버튼을 누르면 RuntimeException 이 발생하여 앱이 크래시하고 “앱이 중단되었습니다”(Unfortunately, application has stopped) 창이 표시됩니다. 크래시 리포트 플러그인 테스트에 가장 간단한 방법입니다.
방법 B: 자바스크립트 호출로 크래시 (네이티브 플러그인에서 예외 던지기)
자바스크립트 호출로 진짜 크래시를 일으키려면 네이티브 플러그인을 만들어 그 execute 메서드 안에서 예외를 던지면 됩니다.
플러그인 생성 참고 문서: http://docs.phonegap.com/en/3.0.0/guide_platforms_android_plugin.md.html
| |
| |
다만, 위처럼 네이티브 플러그인 내부에서 예외를 던져도, 앞서 설명한 Cordova 내부 catch 계층이 예외를 삼켜버립니다(콘솔에 크래시 로그만 남고 앱은 계속 동작). 따라서 다음의 catch 블록을 제거해야 예외가 부모로 전파되어 크래시가 됩니다.
1. PluginManager 클래스의 execHelper 메서드에서 catch 블록 제거
| |
2. ExposedJsApi 클래스의 exec 메서드에서 catch 블록 제거
| |
이 변경을 적용하면 자바스크립트로부터 플러그인을 호출했을 때 예외가 삼켜지지 않고 네이티브 런타임까지 전파되어 앱이 크래시하게 됩니다.
방법별 비교
| 구분 | 동작 방식 | 사용 시점 |
|---|---|---|
| 방법 A (onCreateOptionsMenu) | 메뉴 버튼 누를 때 RuntimeException | 수동 조작으로 크래시 재현이 필요한 경우 |
| 방법 B (네이티브 플러그인) | JS 호출 → 플러그인 execute → 예외 전파 | 자바스크립트 트리거로 크래시가 필요한 자동 테스트 |
5. 향후 예방 조치
크래시 기반 테스트와 크래시 리포트 플러그인 개발 시 다음을 참고합니다.
- JS 예외로는 절대 네이티브 크래시를 만들 수 없다는 점을 명심합니다. 크래시 테스트는 반드시 네이티브(Java) 레벨의 uncaught exception이 필요합니다.
- Cordova 내부 catch 계층 (
PluginManager.execHelper,ExposedJsApi.exec) 의 존재를 숙지하고, 플러그인 테스트 시 예외가 삼켜지는 원인을 이 구조에서 우선 찾습니다. - 테스트 목적으로만 Cordova 프레임워크 소스의 catch 블록을 제거하고, 프로덕션 빌드에서는 원본 소스로 복원하는 것을 권장합니다. 프레임워크 내부 예외 처리를 영구적으로 제거하면 다른 플러그인의 오류가 그대로 노출되어 안정성이 저하될 수 있습니다.
- 실제 운영 환경의 크래시 오류 분석보다는, 크래시 리포터 플러그인의 수집·전송 파이프라인을 검증할 때 이 강제 크래시 방법을 활용합니다.
출처
-원본 질문 및 해결책: How to make my phonegap android app crash? — StackOverflow