본문 바로가기

프로그래밍/안드로이드

UncaughtExceptionHandler에 대한 정리

UncaughtExceptionHandler에 대한 정리


어느날 지인으로 부터 안드로이드 관련 프로젝트중 일부분에 대해 코드 수정을 의뢰 받은적이 있습니다. 그분은 실행을 해보면 아무런 이상이 없고 아무런 에러가 발생하지 않는데 시스템이 자꾸 죽어 버린다고..... 


테스트를 해보니 ANR이 발생하더군요. 그것도 인위적으로 추가해 놓은 예외 코드와 에러코드에서 말입니다.  첨에는 암담 하더군요.


문득 예전에 프로젝트 중에 사용했던 코드가 문득 떠오르더군요.(사실 업무의 효율성을 위해서 라기 보다는 자체 솔루션 개발중에 다른팀을 골탕먹이기 위해 약간의 작업을 했던 것이죠).


이름에서 알수 있듯이 UncaughtExceptionHandler은  캐치되지 않은 런타임 예외를 처리하는 것입니다.  이녀석은 J2SE 5.0에서 추가된 녀석입니다.


 SDN Korea에서 검색해서 나온 아래 설명입니다.
 

2004년 3월 25일자 테크팁 예외 처리의 Best Practices에서 예외 처리를 위한 몇 가지 선진 기법을 설명한 바 있다. 이번 테크팁에서는 예외를 처리하는 또 다른 방법에 관해 배우게 될 것인데, J2SE 5.0에 추가된 UncaughtExceptionHandler가 바로 그것이다.



이름에서 알 수 있듯이 UncaughtExceptionHandler는 캐치되지 않은 예외를 처리하는 방법인데, 좀더 구체적으로 얘기하면 캐치되지 않은 런타림 예외를 처리하는 것이다. 자바 컴파일러는 모든 비(非) 런타임 예외를 처리할 것을 요구하며, 그렇지 않을 경우 프로그램은 컴파일되지 않는다. 여기서 '처리한다'는 것은 예외가 선언하는 메소드의 throws 절에서 선언되거나 try-catch 블록의 catch 절에서 캐치되는 것을 의미한다.



그럼 예시를 위해 다음 두 가지 예외를 살펴보기로 하자: FileNotFoundException과 ArithmeticExceptionString이나 File 인자로FileReader 생성자를 호출할 때, 제공된 장소가 유효한 정상 파일을 가리키지 않을 경우 FileNotFoundException이 throw된다. 컴파일러는 이 생성자들 중 하나를 호출할 때 throw된 예외를 처리할 것을 요구한다.



FileReader input;
String filename = ...;
try {
input = new FileReader(filename);
} catch (FileNotFoundException e) {
processMissingFile(filename, e);
}

이와 대조적으로, ArithmeticException은 일종의 런타임 예외라 할 수 있는데, 자바 프로그래밍 언어 스펙은(컴파일러도 마찬가지로) 그 런타임 예외를 처리할 것을 요구하지 않는다. 따라서 10에서 0까지의 수를 100으로 나누는 다음 루프의 경우 루프를 통과하는 최종 패스에ArithmeticException을 throw한다:



for (int i=10; i >= 0; i--) {
int div = 100 / i;
}

Exception in thread "main" java.lang.ArithmeticException: /
by zero
at Test.main(Test.java:4)

기본 Uncaught Exception Handler의 역할은 스택 트레이스를 프린트하는 것인데, 보통 이 기본값 동작으로 충분하지만 꼭 그렇지 않은 경우도 있다. 스택 트레이스를 시스템 콘솔에 덤프하는 대신 팝업 창에 트레이스를 표시한다고 생각해보자. 이럴 때는 자체의 기본 Uncaught Exception Handler를 설치하면 이 작업을 수행할 수 있다.



Uncaught Exception Handler 설치에는 최소 세 가지 방법이 사용되고 있다. 첫째는 Thread의 setUncaughtExceptionHandler() 메소드를 호출하는 것인데, 이 경우 특정 스레드의 동작을 커스터마이즈할 수 있게 된다. 둘째로, 자체 ThreadGroup을 정의하고 그룹 내에 생성된 스레드의 uncaughtException() 메소드를 오버라이드하여 해당 스레드의 동작을 변경할 수 있다. 셋째로, Thread의 정적setDefaultUncaughtExceptionHandler() 메소드를 호출하여 모든 스레드의 기본값 동작을 설정할 수 있다.



Thread의 setUncaughtExceptionHandler() 및 setDefaultUncaughtExceptionHandler() 메소드 모두UncaughtExceptionHandler 인터페이스 인자의 구현을 수용한다. 이 인터페이스는 Thread 클래스의 내부 인터페이스이므로 정식 명칭은Thread.UncaughtExceptionHandler이며, 하나의 메소드를 가진다.



void uncaughtException(Thread t, Throwable e)

uncaughtException 메소드의 구현을 인터페이스의 커스텀 구현의 일부로서 또는 ThreadGroup의 오버라이드된 메소드로서 제공하여 커스터마이즈된 동작을 얻을 수 있다. 예시를 위해, 런타임 예외와 마주칠 때마다 텍스트 영역의 말미에 첨부되는 스택 트레이스가 포함된 창을 표시하는 UncaughtExceptionHandler의 구현을 살펴보기로 하자. 예외 사이에 창을 닫을 수 있으며, 창은 다음 예외가 발생할 때 다른 창 앞에 다시 표시된다



import java.awt.*;
import java.io.*;
import javax.swing.*;

public class StackWindow extends JFrame
implements Thread.UncaughtExceptionHandler {

private JTextArea textArea;

public StackWindow(
String title, final int width, final int height) {
super(title);
setSize(width, height);
textArea = new JTextArea();
JScrollPane pane = new JScrollPane(textArea);
textArea.setEditable(false);
getContentPane().add(pane);
}

public void uncaughtException(Thread t, Throwable e) {
addStackInfo(e);
}

public void addStackInfo(final Throwable t) {
EventQueue.invokeLater(new Runnable() {
public void run() {
// Bring window to foreground
setVisible(true);
toFront();
// Convert stack dump to string
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
t.printStackTrace(out);
// Add string to end of text area
textArea.append(sw.toString());
}
});
}
}

핸들러를 테스트하기 위해서는 핸들러를 설치한 다음 런타임 예외를 throw하는 프로그램이 필요한데, 다음의 프로그램 DumpTest가 이 작업을 수행한다. 단순성을 감안하여, DumpTest는 2개의 예외만을 생성한다. 사용자 여러분은 더 난해한 코드를 마음대로 프로그램에 추가하여 더 많은 예외가 throw되도록 한다. 이 때, 프로그램은 예외 사이에서 일시 중지됨으로써 사용자가 예외 사이에 예외 덤프 스택 창을 닫을 수 있음을 보여준다.



import java.io.*;

public class DumpTest {
public static void main(final String args[])
throws Exception {
Thread.UncaughtExceptionHandler handler =
new StackWindow("Show Exception Stack", 400, 200);
Thread.setDefaultUncaughtExceptionHandler(handler);
new Thread() {
public void run() {
System.out.println(1 / 0);
}
}.start();
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));
System.out.print("Press Enter for next exception");
br.readLine();
new Thread() {
public void run() {
System.out.println(args[0]);
}
}.start();
System.out.print("Press Enter to end");
br.readLine();
System.exit(0);
}
}

StackWindow와 DumpTest를 컴파일한다. DumpTest 실행 시 콘솔에 다음과 같은 내용이 표시되어야 한다.



> java DumpTest
Press Enter for next exception

아울러, 텍스트 영역의 예외를 위한 스택 트레이스가 포함된 창이 표시된다.



UncaughtExceptionWindow Window



Enter를 누르면 콘솔에 다음과 같은 내용이 표시되어야 한다.



Press Enter to end

또한, 창 내의 텍스트 영역에 첨부된 또 다른 스택 트레이스가 표시되어야 한다.



UncaughtExceptionWindow Window



캐치되지 않은 예외를 처리하는 작업은 이것 말고도 더 여러 가지가 있다. Modal Dialogs(여러가지 양식의 대화상자)의 경우에는 자체 이벤트 스레드를 요구하며, 따라서 자체 Uncaught Handler가 필요하다. 시스템 속성 sun.awt.exception.handler는 모든 경우를 커버할 수는 있지만 문서화 수준이 아직 미흡한 상태이다. 한편, 속성을 정식 API에 포함시키기 위해 RFE(Request for Enhancement)가 이미 제출된 상태이다.



예외 처리의 Best Practices 외에도, 2002년 5월의 StackTraceElements에 관한 팁에서도 유용한 참고 정보를 얻을 수 있다. 또한, 자바 튜토리얼에 포함된 레슨 Handling Errors Using Exceptions도 함께 참조할 것.




출처 : http://greenfrog7.egloos.com/959768