[reference site]
Know the JVM Series – 1 – The Uncaught Exception Handler
Know the JVM Series – 2 – Shutdown Hooks
question : how to catch Error globally on android?
배경
안드로이드 앱을 개발하고, 앱을 배포를 합니다. 하지만 고려하지 않던 Exception이 발생하면 앱은 죽게됩니다. 왜 죽었는지 오류를 수집하지 않는다면, 앱의 인지도는 떨어지기 마련입니다. 그렇다면 고려하지 않던 Exception이 발생하면 어떻게 해야할까요?
고려하지 않던 예외를 잡아주는 UncaughtExceptionHandler 클래스
이 기능은 자바에서 제공해주는 기능입니다. 사용되는 클래스는 Thread 클래스와 UncaughtExceptionHandler 클래스 입니다.
아래는 예제로 작성한 CustomUncaughtExceptionHandler 클래스 입니다.
[CustomUncaughtExceptionHandler]
public class CustomUncaughtExceptionHandler implements UncaughtExceptionHandler {
private Context mContext;
private UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
public CustomUncaughtExceptionHandler(Context context) {
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
mContext = context;
}
//@Override
public void uncaughtException(Thread thread, Throwable ex) {
final String logMsgParams = makeStackTrace(thread, ex);
//TODO: to process exception for reporting.
callDefaultUncaughtExceptionHandler(thread, ex);
}
private static String makeStackTrace(Thread thread, Throwable ex) {
StringBuilder errLog = new StringBuilder();
errLog.append("FATAL EXCEPTION: " + thread.getName());
errLog.append("\n");
errLog.append(ex.toString());
errLog.append("\n");
StackTraceElement[] stack = ex.getStackTrace();
for (StackTraceElement element : stack) {
errLog.append(" at " + element);
errLog.append("\n");
}
StackTraceElement[] parentStack = stack;
Throwable throwable = ex.getCause();
while (throwable != null) {
errLog.append("Caused by: ");
errLog.append(throwable.toString());
errLog.append("\n");
StackTraceElement[] currentStack = throwable.getStackTrace();
int duplicates = countDuplicates(currentStack, parentStack);
for (int i = 0; i < currentStack.length - duplicates; i++) {
errLog.append(" at " + currentStack[i].toString());
errLog.append("\n");
}
if (duplicates > 0) {
errLog.append(" ... " + duplicates + " more");
}
parentStack = currentStack;
throwable = throwable.getCause();
}
return errLog.toString();
}
private static int countDuplicates(StackTraceElement[] currentStack, StackTraceElement[] parentStack) {
int duplicates = 0;
int parentIndex = parentStack.length;
for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0;) {
StackTraceElement parentFrame = parentStack[parentIndex];
if (parentFrame.equals(currentStack[i])) {
duplicates++;
} else {
break;
}
}
return duplicates;
}
private void callDefaultUncaughtExceptionHandler(Thread thread, Throwable ex) {
if (mDefaultUncaughtExceptionHandler != null) {
mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
}
}
}
[CustomActivity]
public class CustomActivity {
...
public void onCreate(Bundle savedInstanceState) {
UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
if ( !(handler instanceof CustomUncaughtExceptionHandler) ) {
Thread.setDefaultUncaughtExceptionHandler(new CustomUncaughtExceptionHandler(context));
Log.i("TEST", "registered CustomUncaughtExceptionHandler");
}
super.onCreate(savedInstanceState);
...
}
...
}
잡을 수 없는 java.lang.Error 클래스들
Thread.setDefaultUncaughtExceptionHandler()를 사용한다고해도 java.lang.Error 클래스의 서브 클래스들은 잡아낼 수가 없습니다. UncaughtExceptionHandler의 uncaughtException(Thread t, Throwable ex)를 보면 Throwable 타입으로 넘겨줘서 Error 클래스도 받을 것 같아 보였지만, 아무리해도 Error는 잡을 수 없더군요.
그래서 Runtime.addShutdownHook(Thread hook)을 사용해서 혹시나 잡을 수 있지 않을까 했지만, 이녀석은 종료가 됐는데도 호출이 되지 않더군요. 안드로이드 개발문서를 보면, 안드로이드 라이프사이클은 VM종료를 포함하지 않는다고 하면서 호출을 보장하지 않는다고 합니다.
Log를 통해서 확인해 본 결과, uncaughtException에 OutOfMemoryError가 들어오기는 하는군요. 즉, catch를 하기는 합니다. 하지만 내부에 구현된 여러 기능이 작동을 안하는군요. 좀 더 테스트 해본 후 다시 올리겠습니다.
해결방법을 알려줘!!!
정상적으로 동작합니다.
기본 UncaughtExceptionHandler
개발자가 Thread.setDefaultUncaughtExceptionHandler()를 사용하여 변경하기 전의 기본 Handler는 RuntimeInit$UncaughtHandler 입니다.
이 Handler 역시 Thread.UncaughtExceptionHandler를 상속 받고 있습니다.
printStackTrace 만드는 형식을 보고 싶다면...
Throwable$printStackTrace() 를 확인해보세요.
Throwable.java 파일은 dalvik/libcore/luni-kernel/src/main/java/java/lang/ 위치에 있습니다.
'안드로이드' 카테고리의 다른 글
[안드로이드] InputStream을 닫는데 왜 오래 걸릴까? (0) | 2013.04.19 |
---|---|
[안드로이드] Dialog.dismiss()를 할 때, java.lang.IllegalArgumentException: View not attached to window manager 예외 회피하기 (2) | 2013.03.08 |
[안드로이드] Collection을 사용 할 때 ConcurrentModificationException을 피하는 방법 (0) | 2013.03.06 |
[안드로이드] APK 파일 SIGN 하기 (0) | 2012.10.29 |
안드로이드 ListView와 Thread를 사용할 때 동기화 오류 (IllegalStateException) (0) | 2012.10.08 |
[안드로이드] Context를 Activity로 강제 타입 캐스팅시 발생하는 문제(Shutting down VM) (0) | 2012.10.02 |
[안드로이드] ListView의 fadingEdge 없애는 방법 (0) | 2012.09.26 |
[안드로이드] /system 디렉토리 remount 하기 (0) | 2012.09.19 |
[안드로이드] 텍스트 파일의 캐릭터 셋(Character Set) 얻기 (0) | 2012.09.04 |
[안드로이드] 이클립스 업데이트 오류 (Install download0) (0) | 2012.07.27 |