지난 번에는 AndroidAnnotations(AA) 오픈소스 라이브러리의 @Click 어노테이션을 흉내내봤습니다. 이번에는 @Background와 @UiThread 어노테이션을 흉내내보죠.
먼저 @Background와 @UiThread 어노테이션 클래스를 작성합니다.
(1) Background.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <pre>
* 메소드를 백그라운드 쓰레드에서 실행해주는 어노테이션.
*
* ※ 메소드 제약사항
* - 메소드 리턴타입은 void만 가능
* - Exception을 던지는 것이 가능하지만 에러정책에 따라 메소드 caller한테는 throw되지 않을 수 있음
*
* ※ NOTE
* - 이 어노테이션은 작업진행상태 스핀다이얼로그 표시, 작업취소 처리, 작업 콜백메소드 등의 기능을 지원하지 않는다.
* </pre>
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Background {
}
(2) UiThread.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <pre>
* 메소드를 UI 쓰레드에서 실행해주는 어노테이션.
*
* ※ delay
* - delay 파라미터 설정시 해당 밀리초 이후에 실행된다. 기본값은 0이다.
*
* ※ 메소드 제약사항
* - 메소드 리턴타입은 void만 가능
* - Exception을 던지는 것이 가능하지만 에러정책에 따라 메소드 caller한테는 throw되지 않을 수 있음
* </pre>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UiThread {
long delay() default 0;
}
자 이제 위 어노테이션이 붙은 메소드를 백그라운드 쓰레드나 UI쓰레드에서 실행시키기 위해서 AOP(AsepctJ)의 힘을 빌려봅니다.
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
* <pre>
* {@link Background}, {@link UiThread} 어노테이션이 붙은 메소드가
* 백그라운드 및 UI쓰레드에서 실행되도록 애스펙트로 처리한다.
* </pre>
*/
@Aspect
public class BackgroundAnnotationAspect {
private Handler uiHandler = new Handler(Looper.getMainLooper()); private Executor threadExecutor = Executors.newCachedThreadPool();
@Around("execution(@com.mycompany.async.Background * *.*(..))")
public void proceedInBackground(final ProceedingJoinPoint pjp) {
threadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
pjp.proceed();
} catch (final Throwable e) {
// 에러정책에 따라 적절히 에러 처리
}
}
});
}
@Around("@annotation(uithread) && execution(@com.mycompany.async.UiThread * *.*(..))") public void proceedInUiThread(final ProceedingJoinPoint pjp, UiThread uithread) { runInUiThread(new Runnable() { @Override public void run() { try { pjp.proceed(); } catch (Throwable e) { // 에러정책에 따라 적절히 에러 처리 }
}
}, uithread.delay());
}
/**
* {@link Runnable}을 UI쓰레드에서 실행한다.
*
* @param run
* @param delayMillis 지연시간. 밀리초
*/
private void runInUiThread(Runnable run, long delayMillis) {
if (delayMillis <= 0) {
// 현재 쓰레드가 UI 쓰레드인 경우 그냥 run 실행.
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
run.run();
return;
}
uiHandler.post(run);
return;
}
uiHandler.postDelayed(run, delayMillis);
}
}
다 됐습니다. 아래처럼 쓰면 됩니다. (꼭 액티비티 내의 메소드에 국한되지 않고 아무 클래스에서든 사용가능합니다)
public class TestAsyncActivity extends Activity {
@Background
private void doBackground() {
URL url = new URL("http://mycompany.com/xxx"); URLConnection conn = url.openConnection(); byte[] contents = FileCopyUtils.copyToByteArray(conn.getInputStream()); doUiThread(new String(contents));
}
@UiThread
private void doUiThread(String contents) {
Toast.makeText(getApplicationContext(), contents, Toast.LENGTH_LONG).show();
}
유용하게 쓰시길 바랍니다.
'Android' 카테고리의 다른 글
[AOP] 아키텍처 규칙위반 체크 예제 (0) | 2013.04.17 |
---|---|
안드로이드 쓰레드 사용 총정리 (0) | 2013.04.15 |
AndroidAnnotations의 @Click 어노테이션 흉내내기 (1) | 2012.04.29 |
[RoboGuice] injection 이후 특정 메소드 실행 방법 (0) | 2012.04.29 |
[RoboGuice] 어노테이션 활용 (0) | 2012.04.29 |