안드로이드에서 쓰레드 사용기법에 대해서 잘 정리된 자료를 발견했습니다.


안드로이드에는 비동기 작업을 위한 여러 API 들이 제공되는데요, 각각의 차이점과 어떤 경우에 사용하면 좋은지에 대해서 깔끔하게 정리가 되어있네요.



Loader는 이 자료를 보면서 처음 알게되네요.


자료 출처 : http://www.slideshare.net/andersgoransson/efficient-android-threading  


Posted by 에코지오
,

지난 번에는 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 {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ... ...

        Button test = ....;
        test.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                doBackground();
            }
        });
    }

    @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();

    }


유용하게 쓰시길 바랍니다.



Posted by 에코지오
,

RoboGuice와 더불어 안드로이드 코딩을 굉장히 심플하게 만들어주는 AndroidAnnotations(AA) 오픈소스 라이브러리는 @Click 이라는 아주 유용한 어노테이션을 제공합니다. 

뷰의 ID가 설정된 어노테이션을 메소드에 선언해주면 설정된 뷰에 click 이벤트가 발생할 때 해당 메소드가 실행됩니다. 

기존에는  view.setOnClickListener(new OnClickListener() { ...}) 와 같은 OnClickListener 익명 클래스로 도배된 지저분한 코딩이 필요했었죠.


@Click(R.id.myButton)  

void myButtonClicked() { 

 ... ... // R.id.myButton을 누르면 이 메소드가 호출됩니다.

}  


@Click

void anotherButton(View clickedView) {

 ... ... // R.id.anotherButton 버튼을 누르면 이 메소드가 호출됩니다.

}


@Click({R.id.myButton1, R.id.myButton2})  

void myButtonsClicked() { 

 ... ... // R.id.myButton1 또는 R.id.myButton2를 누르면 이 메소드가 호출됩니다.

}  


AA를 사용하지 않는(사용하기 싫은?) 환경을 위해 비슷한 기능을 제공하는 우리만의 @Click을 만들어 보겠습니다. 

먼저 Click 어노테이션을 작성합니다. value는 클릭 이벤트가 발생하는 뷰의 id 값입니다.


@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Click {

    int[] value() default -1;

}


@Click 어노테이션을 선언하는 메소드는 아래의 제약사항을 준수해야 합니다(AA의 @Click도 마찬가지입니다).

- 리턴 타입은 void만 가능

- 파라미터는 없거나 View 타입의 1개만 가능

- Exception을 던지는 것이 가능하지만 메소드 caller한테는 throw되지 않음 (AA에서는 아예 checked Exception도 못던집니다).


그리고 공통 액티비티 클래스에서 android.app.Activity의 onContentChanged() 메소드를 오버라이딩합니다.


    /**

     * 어노테이션에 설정된 뷰에 대해 클릭 이벤트 발생시 해당 메소드가 실행되도록 리스너 등록.

     *

     */

    @Override

    protected void onContentChanged() {

        super.onContentChanged();

        processClick(this);

    }


    public static void processClick(final Activity activity) {

        Class clazz = activity.getClass();


        for (final Method m : clazz.getDeclaredMethods()) {

            Click click = m.getAnnotation(Click.class);//m.isAnnotationPresent(Click.class)

            if (click == null)

                continue;


            Class<?>[] params = m.getParameterTypes();

            final boolean hasViewParameter = (params.length > 0);


            int[] ids = click.value();

            for (int id : ids) {

                View view = activity.findViewById(id);

                if (view != null) {

                    view.setOnClickListener(new OnClickListener() {

                        public void onClick(View view) {

                            try {

                                if (hasViewParameter) {

                                    m.invoke(activity, view);

                                } else {

                                    m.invoke(activity);

                                }

                            } catch (IllegalAccessException e) {

                                throw new RuntimeException(e);

                            } catch (InvocationTargetException e) {

                                throw new RuntimeException(e);

                            }

                        }

                    });

                }

            }


        }

    }


다 됐습니다. 공통 액티비티를 상속받은 액티비티에서 AA의 그것과 동일한 기능을 수행하는 @Click 어노테이션을 사용할 수 있습니다. 간단하죠?


※ 단, AndroidAnnotations의 @Click과 차이점이 몇가지 있습니다.

- AA에서는 setContentView()에 @Click 처리코드가 작성되지만, 위에서는 onContentChanged()에서 @Click을 처리하도록 했습니다. 

- AA의 @Click은 뷰의 id가 주어지지 않은 경우 메소드 이름으로 뷰 id를 자동으로 찾지만 위 코드는 그러한 기능은 제공하지 않습니다.

- AA에서는 컴파일타임에 Java Annotation Processing에 의해 @Click 처리 코드가 생성되지만 위 코드는 런타임에 @Click이 붙은 메소드를 이벤트리스너를 등록합니다.



Posted by 에코지오
,

AndroidAnnotations는 어떤 객체에 대한 주입(injection)이 완료된 이후에 코드를 실행할 수 있는 @AfterInject 어노테이션을 제공합니다. 

그리고  GuiceyFruitMycilaGuice 같은 라이브러리도 인젝션에 대한 콜백을 받을 수 있는 JSR250의 @PreDestroy, @PostConstruct 어노테이션을 구현하여 제공합니다.

그러나 대표적인 안드로이드용 Injection 프레임워크인 RoboGuice는 객체가 생성되고 필드에 대한 주입이 모두 끝난 이후 지정된 메소드(ex. 초기화 메소드)를 실행하는 깔끔한 방법을 제공하지 않습니다. 


다행히 방법이 전혀 없는 것은 아닙니다. 2가지 방법이 존재합니다.


1. InjectionListener를 이용

유연한 방법은 아니지만 Guice의 InjectionListener를 구현하여 주입이후에 지정된 메소드를 실행하게 할 수 있습니다.

(아래 코드에서는 MyInitClass 객체에 대한 모든 주입이 끝나고 나서 init 메소드를 실행합니다)


bindListener(Matchers.subclassesOf(MyInitClass.class), new TypeListener() {

    @Override

    public <I> void hear(final TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {

        typeEncounter.register(new InjectionListener<I>() {

            @Override

            public void afterInjection(Object i) {

                MyInitClass m = (MyInitClass) i;

                m.init();

            }

        });

    }

});



2. @Inject를 이용

Guice는 필드에 대한 모든 주입을 끝내고 나면 @Inject가 붙은 메소드를 실행한다고 합니다. 


    @Inject

    protected void init() {

       ... ...

    }


이 방법은 약간의 트릭에 해당하지만 매우 간단하기 때문에 저는 이 방법을 사용합니다. 자세한 내용은 다음 링크를 참고하세요.


http://stackoverflow.com/questions/2093344/guice-call-init-method-after-instantinating-an-object



Posted by 에코지오
,

RoboGuice 사용시에 클래스 타입만으로는 주입받을 모듈을 구분하기 어려운 경우가 있습니다. 

예들들어 시간이 오래걸리는 작업을 처리하는 동안 사용자에게 진행중임을 알려주는 다이얼로그가 필요해서 아래처럼 Dialog를 주입받는 코드를 작성했습니다.


@Inject

protected Dialog progressDialog;


위 코드에서 우리가 원하는 것은 작업이 진행중임을 알려주는 Dialog를 주입시키는 것입니다. 그러나 다른 모듈에서는 다른 모양의 Dialog가 주입되어야 하는데 저 Dialog 타입만으로는 그것을 구분할 수 없습니다.


이런 상황에서는 추가적인 정보를 제공하는 어노테이션을 이용하면 됩니다. 위 예제의 경우 먼저 @TaskProgressDialog와 같은 어노테이션을 만들어 추가해줍니다.  


@Inject

@TaskProgressDialog

protected Dialog progressDialog;


그리고 Guice 모듈설정에서 AnnotatedBindingBuilder.annotatedWith() 메소드를 통해 해당 어노테이션이 붙은 놈에 적절한 모듈을 주입합니다.


protected void configure() {

  bind(Dialog.class).annotatedWith(TaskProgressDialog.class).toProvider(TaskProgrerssDialogProvider.class);

  ... ...

}


이렇게 하면 Dialog 타입이면서 @TaskProgressDialog 어노테이션이 붙어 있는 놈한테만 TaskProgressDialogProvider가 생성한 Dialog 객체가 주입될 것입니다.


* 참고로, Guice는 문자열을 통해 어노테이션을 구분할 수 있는 @Named 어노테이션을 제공합니다. 그러니까 필요시마다 TaskProgressDialog 같은 별도의 어노테이션 클래스를 만들 필요가 없다는 얘기입니다. 단, 문자열이 이용되므로 보조적인 수단으로만 사용을 권장합니다.


public class RealBillingService implements BillingService {

  @Inject

  public RealBillingService(@Named("Checkout") CreditCardProcessor processor,

      TransactionLog transactionLog) {

    ...

  }


Guice 모듈설정에서는 Names.named() 를 이용합니다.


bind(CreditCardProcessor.class).annotatedWith(Names.named("Checkout")).to(CheckoutCreditCardProcessor.class);






Posted by 에코지오
,

RoboGuice 사용환경에서 AspectJ의 aspect 클래스에 모듈을 injection하는 방법입니다.



방법1. static 방식으로 injection


(1) 애스펙트 클래스에서 static 멤버변수로 선언

주입할 모듈을 static 멤버변수로 선언합니다.


 @Inject protected static ContextScopedProvider<T> tProvider; // T는 @ContextSingleton이어야함

 @Inject protected static ExceptionHandler exceptionHandler;//Singleton


(2) Guice 모듈 설정에서 static injection 처리

configure() 메소드에서 requestStaticInjection 메소드를 통해 주입합니다.


requestStaticInjection(ExceptionHandlingAspect.class);


그러나 이 방법은 권장하지 않습니다. Guice에서도 static injection은 deprecated될 것이라고 합니다.

http://code.google.com/p/google-guice/wiki/AvoidStaticState



방법 2. aspect 객체를 구하여 injection


aspect는 AspectJ에 의해 인스턴스화되며 우리가 직접 생성할 수 없습니다. 대부분의 경우 aspect는 싱글턴 객체이며, AspectJ는 싱글턴 aspect 객체를 참조할 수 있는 Aspects.aspectOf() 메소드를 제공합니다.


(1) 주입할 모듈을 애스펙트 클래스에 보통의 멤버변수로 선언


 @Inject protected ExceptionHandler exceptionHandler;


(2) Guice 모듈 설정에서 requestInjection을 통해 injection 처리


requestInjection(org.aspectj.lang.Aspects.aspectOf(ExceptionHandlingAspect.class));


참고:

Posted by 에코지오
,

AndroidAnnotations 라이브러리와 비교하여 RoboGuice의 단점으로 지적되고 있는 사항을 해결하기 위한 방안입니다.



런타임시에 리플렉션(reflection) 사용에 따른 성능하락

- 스마트폰 하드웨어 성능이 점점 강해지고 있으므로 앞으로 큰 문제는 안될 것임

  또한 Dalvik VM의 리플렉션 성능이 점점 좋아지고 있음

- 게임처럼 대부분의 기능이 스마트폰 내에서 처리되는 앱이라면 문제될 수 있음

  그러나 주로 네트워크를 통해 서버측에서 결과를 받아 단순히 표출하는 클라이언트 앱의 경우에는 

  응답시간의 대부분은 네트워크 소요시간이 차지하며, 폰 로컬의 리플렉션 처리가 차지하는 시간은 매우 작을 것으로 판단됨. 

  따라서 클라이언트 앱에서 RoboGuice의 런타임 리플렉션은 큰 문제가 아님.



앱 용량이 많이 커지는 문제

- RoboGuice를 사용하기 위해 필요한 guice-3.0.jar 파일과 roboguice-2.0.jar 파일의 크기를 합치면 거의 820KB임.

- 반면, androidannotations-2.5.jar 파일은 53KB에 불과함

- 그러나 실제로 apk의 용량이 820KB만큼 커지는 것은 아니며 ProGuard를 적절히 설정하면 apk 파일 크기가 생각보다 많이 늘어나지 않음



RoboActivity 상속에 따른 Activity 클래스의 유연성 부족

- AndroidAnnotations에는 Activity에 @EActivity 어노테이션을 선언만하면 되기 때문에 Activity가 다른 액티비티를 상속받는 것이 가능함

- 그러나 RoboGuice에서는 RoboActivity를 상속받아야 하므로 다른 액티비티를 상속받을 수 없음

- 따라서 AOP를 통해서 다른 액티비티를 상속받지 않고도 기능을 추가할 수 있는 방법을 적용하는 것을 고려



유틸리티성 어노테이션 부족

- AndroidAnnotations는 @ViewById 같은 injection용 어노테이션 외에 @Click, @Rest, @Background, @UiThread 등의 다양한 유틸리티성 어노테이션을 제공함

- 반면 RoboGuice는 코드를 줄여주는 이러한 유틸리티가 별로 없음

- AndroidAnnotations 소스를 참고하여 유틸리티성 어노테이션을 처리하는 AOP 애스펙트를 개발하여 적용하면 됨(별로 어렵지 않음)

- @Click : (공통) 액티비티의 setContentView() 메소드에 after 어드바이스로 처리

- @Background, @UiThread : 메소드에 around 어드바이스로 처리

- @Rest : ... 고민중...






Posted by 에코지오
,

UI 쓰레드가 아닌 일반 쓰레드에서 UI 작업처리를 위해 new Handler() 를 통해 Handler를 사용할 경우 아래와 같은 에러가 발생합니다.


java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


그럼 UI처리를 위한 Handler는 UI 쓰레드에서만 생성해야 한다고 생각할 수 있습니다.

http://huni.springnote.com/pages/5402287



그러나 그렇지 않습니다. 임의의 쓰레드에서도 UI작업용 Handler를 사용할 수 있습니다.

http://blog.naver.com/PostView.nhn?blogId=rgjoon&logNo=90094338577



다만, UI 작업을 처리하기 위해서는 아래와 같이 UI쓰레드에 바인딩된 Handler를 만들어야 합니다.

http://stackoverflow.com/questions/6369287/accessing-ui-thread-handler-from-a-service


Handler handler = new Handler(Looper.getMainLooper());




* 참고

http://www.aviyehuda.com/2010/12/android-multithreading-in-a-ui-environment/

http://babtingdev.tistory.com/276


Posted by 에코지오
,

이클립스 프로젝트에 RoboGuice(RG) 2.0 설치하기

- 안드로이드프로젝트의 libs에 roboguice-2.0.jar, guice-3.0-no_aop.jar, javax.inject-1.jar 파일을 넣고 빌드패스에 추가

- RG 2.0에서는 RoboApplication 상속받은 Application을 작성해서 AndroidManifest.xml에 등록할 필요는 없음

  


Activity 작성

- RoboActivity 상속받은 Activity 작성

- @InjectView , @Inject 등의 어노테이션 사용



모듈 주입 커스터마이징

- AbstractModule 클래스를 상속받는 클래스 작성

- configure() 메소드에서 커스텀 바인딩 정의 : 자세한 구현은 Google Guice 문서 참조

- res/values/roboguice.xml에 아래처럼 컴스텀 모듈 등록


<?xml version="1.0" encoding="utf-8"?>

  <resources> 

    <string-array name="roboguice_modules">

    <item>com.mypackage.MyModule</item>

  </string-array>

</resources>




인스턴스 스코프

RG에 의해 생성 및 관리되어 다른 클래스에 주입되는 클래스의 인스턴스 스코프(생명주기) 설정


1. Singleton 스코프

- 앱에서 하나의 인스턴스만 존재

- 클래스에 @Singleton 어노테이션 선언 : Guice에서 제공

- 어떤 클래스에서 이 객체를 요청하든 동일한 인스턴스가 제공됨. 그러므로 쓰레드세이프하게 작성해야 함.

- Application 컨텍스트는 직접 주입 받을 수있음(특정 Activity, Service 등 current context랑 관계없는 global context)

- current context(액티비티 컨텍스트)를 주입받으려면 Provider<Context>를 사용해야 함

- 싱글턴 스코프를 가진 기본제공 모듈 : Application, ViewListener, Resources 등?

=> 그러나 RG에서 제공하는 대부분의 모듈은 context를 필요로 하므로 ContextSingleton 스코프를 갖는다.

- current context가 필요없거나 또는 Application 컨텍스트만 필요한 클래스에 적합


2. ContextSingleton 스코프

- current context(액티비티 컨텍스트)에 대해서 하나의 인스턴스만 존재

- 클래승 @ContextSingleton 어노테이션 선언 : RG에서 제공하는 스코프

- current context가 필요한 클래스는 ContextSingleton으로 선언

- 동일 액티비티 객체에 대해서 동일 인스턴스 제공(모든 액티비티에 대해서 동일 인스턴스 제공은 아님)

- Application, Context, Activity, Service 등 @Singleton/@ContextSingleton 클래스를 직접 주입받을 수 있음

- 컨텍스트싱글턴 스코프를 가진 기본제공 모듈 : Context, Activity, Service, AssetManager, ContentResolver,

   LayoutInflator 및 기타 시스템서비스 모듈들

- current context가 필요한 클래스에 적합


3. 프로토타입 스코프

- 요청할 때마다 새로운 인스턴스 생성

- 클래스에 별도의 스코프 어노테이션 선언 없음. (프로토타입이란 용어는 제가 스프링에서 따온 것임)

- 주입될 때마다 또는 provider.get() 할 때마다 새 인스턴스가 생성됨

- 스코프가 설정돼있지 않고 public 기본생성자가 존재하는 모든 클래스가 가능

- Handler처럼 Provider에서 get() 요청시마다 새로운 인스턴스를 리턴하는 클래스

- context가 필요없고 상태를 가진 클래스에 적합


스코프 지정 방법

 (1) 구현체 클래스에 @Singleton 붙임

 (2) @Provides 붙은 메소드에 @Singleton 붙임

 (3) bind().xxx().in(Singleton.class);


주요 기본제공 클래스의 스코프

http://code.google.com/p/roboguice/wiki/ProvidedInjections



어떤 컨텍스트를 주입받을 것인가 

- http://code.google.com/p/roboguice/wiki/InjectingContexts

- current context가 필요한게 아니라면 명시적으로 Application을 주입받아라

- Activity가 필요하다면 Activity를 주입받아라

- 잘 모르겠으면 Context를 주입받아라



Inject 되기 위한 생성자 조건

- 기본 생성자가 존재하거나 

- Inject가능한 파라미터로만 구성되고 @Inject 선언된 생성자가 존재


  @Inject

  SomeClass(Context context, MyComponent component) {}

  // 여기서 context와 component는 inject 가능해야함...



일반 POJO 클래스에 다른 객체들을 주입하는 방법

1. POJO 생성을 RG가 처리하는 경우 

- POJO 내부의 @Inject로 선언된 멤버들은 자동 주입됨

- POJO 클래스가 RoboActivity를 상속받는 액티비티의 @Inject 멤버변수로 선언돼있는 경우 등


2. POJO 생성을 RG가 처리하지 않는 경우(개발자 또는 라이브러리에서 직접 생성하는 경우)

- POJO 생성자 등에서 RoboGuice.injectMembers(context, this)를 이용하여 수동으로 주입함



명시적인 injector 사용을 피하는 방법(생성자 등 초기화 메소드를 핸들링할 수 없는 경우)

- 클래스 또는 Provider, ContextScopedProvider를 static하게 inject 받음

- http://code.google.com/p/roboguice/wiki/HowToInjectXYZ

 

 @Inject protected static ContextScopedProvider<T> tProvider; // T는 @ContextSingleton이어야함

 @Inject protected static ExceptionHandler;//singleton


=> 단, MyModule에서 requestStaticInjection(POJO.class)을 해주어야 함

=> 그러나 RG 2.0에서는 static injection을 비권장 ( http://code.google.com/p/roboguice/wiki/UpgradingTo20 )

=> static injection은 context에 액세스할 수 없는 클래스 및 static 메소드를 가진 헬퍼나 유틸 클래스에 적합할 듯.

(http://www.rosscode.com/blog/index.php?title=android-development-part-3-wiring-up-roboguice&more=1&c=1&tb=1&pb=1 )



Provider란 무엇인가

- 인스턴스 생성이 복잡한 경우 인스턴스를 만드는 방법을 제공

- 예를들어 new MyPojo()로 객체 생성후 myPojo.init() 등의 추가 작업한 인스턴스를 리턴

- MyPojo를 주입받는 모듈은 초기화 완료된 MyPojo 인스턴스를 받게 됨

- Module에서 @Provides 어노테이션을 쓰는 메소드를 작성해도 됨(Provider 인터페이스 구현안해도 됨)

- http://code.google.com/p/google-guice/wiki/ProvidesMethods




명시적/묵시적 바인딩

- 명시적 바인딩 : Module에서 bind().to(), bind().toProvider()로 바인딩하는 것

- 묵시적 바인딩 : 

   (1) 기본생성자나 inject가능한 생성자를 가진 구현 클래스가 존재하는 경우

   (2) @ImplementedBy를 통해 기본 구현체를 지정한 경우

   (3) @ProvidedBy를 통해 구현체 인스턴스를 생성해줄 Provider를 지정한 경우



AOP 애스펙트에도 RG의 inject가 가능한가

- 가능함. Aspects.aspectOf()를 통해 aspect 싱글턴을 구하여 Guice 모듈에서 강제 inject시켜줌.



Injection 이후에 지정된 메소드 호출

- Guice의 TypeListener를 이용하거나 GuiceyFruit 또는 MyciaGuice의 @PostConstruct를 사용

- http://stackoverflow.com/questions/2093344/guice-call-init-method-after-instantinating-an-object

- http://code.google.com/p/mycila/wiki/MycilaGuice

- http://code.google.com/p/guiceyfruit/




* 참고

RoboGuice 2.0 설정 방법 : http://code.google.com/p/roboguice/wiki/UpgradingTo20


Posted by 에코지오
,

1. RoboGuice 2.0

- RoboActivity의 onContentChanged() 메소드에서 View 주입(inject)


    @Override

    public void onContentChanged() {

        super.onContentChanged();

        RoboGuice.getInjector(this).injectViewMembers(this); //View 주입

        eventManager.fire(new OnContentChangedEvent());

    }



2. AndroidAnnotations 2.5

- 언더바 붙은 Activity(Java Annotation Processing에 의해 생성)의 setContentView() 메소드에서 View 주입 및 @Click 처리


    @Override

    public void setContentView(int layoutResID) {

        super.setContentView(layoutResID);

        afterSetContentView_();

    }



    private void afterSetContentView_() {

        //View 주입 코드

        mSaveBtn = ((Button) findViewById(id.save_btn)); 

        // ... ...


        // @Click 처리 코드

        {

            View view = findViewById(id.save_btn);

            if (view!= null) {

                view.setOnClickListener(new OnClickListener() {



                    public void onClick(View view) {

                        save();

                    }


                }

                );

            }

        }

        //... ...


        init();

        //... ...

    }


Posted by 에코지오
,