Android UncaughtExceptionHandler
안드로이드 애플리케이션에서 예외가 발생하면 다음과 같은 다이얼로그가 뜨게 된다.
안드로이드에서 애플리케이션에서 캐치하지 않은 예외가 발생하면 처리하는 기본 ExceptionHandler는 다음과 같다.
UncaughtExceptionHandler의 구현체는 RuntimeInit 클래스에 Inner 클래스로 구현되어있다.
/**
* Use this to log a message when a thread exits due to an uncaught
* exception. The framework catches these for the main threads, so
* this should only matter for threads created by applications.
*/
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
try {
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
if (mCrashing) return;
mCrashing = true;
if (mApplicationObject == null) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
final String processName = ActivityThread.currentProcessName();
if (processName != null) {
message.append("Process: ").append(processName).append(", ");
}
message.append("PID: ").append(Process.myPid());
Clog_e(TAG, message.toString(), e);
}
// Try to end profiling. If a profiler is running at this point, and we kill the
// process (below), the in-memory buffer will be lost. So try to stop, which will
// flush the buffer. (This makes method trace profiling useful to debug crashes.)
if (ActivityThread.currentActivityThread() != null) {
ActivityThread.currentActivityThread().stopProfiling();
}
// Bring up crash dialog, wait for it to be dismissed
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
if (t2 instanceof DeadObjectException) {
// System process is dead; ignore
} else {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
// Even Clog_e() fails! Oh well.
}
}
} finally {
// Try everything to make sure this process goes away.
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
UncaughtExceptionHandler 가 캐치하지 않은 예외 상황을 처리하는 단계는 다음과 같다.
- 프로세스가 크래시 상태라는 것을 알려준다.
- 메시지에 스레드 이름과 프로세스 이름 등을 로그로 전달한다.
- ActivityManagerNative를 구현하고 있는 클래스에게 크래시가 발생했으니 다이얼로그를 보여주고 입력 받도록 요청한다.
- finally 부분에서 애플리케이션의 프로세스를 종료시킨다.
UncaughtExceptionHandler 사용하기
- MainApplication
public class MainApplication extends Application {
private static final String TAG = MainApplication.class.getSimpleName();
private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
public Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
return mDefaultUncaughtExceptionHandler;
}
@Override
public void onCreate() {
super.onCreate();
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Log.d("MainApplication", "" + mDefaultUncaughtExceptionHandler);
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler());
}
private class UncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
Log.d(TAG, "Crash!!!");
// Try everything to make sure this process goes away.
// android.os.Process.killProcess(android.os.Process.myPid());
// System.exit(10);
mDefaultUncaughtExceptionHandler.uncaughtException(t, e);
}
}
}
killProcess를 호출하여 앱을 종료할 수도 있지만 크래시 로그를 전송한 후에 mDefaultUncaughtExceptionHandler에게 다시 넘겨주어 예외 다이얼로그를 띄우는 것도 좋겠다.
AlarmManager를 사용하여 재시작 하기
로그를 남기거나 어플리케이션을 재실행하려면 AlarmManager를 사용하면 좋겠다.
Intent restartIntent = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent runner = PendingIntent.getActivity(getApplicationContext(), 99, restartIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 5000, runner);