Sqlite는 안드로이드를 사용하면서 가장 많이 사용하는 db가 아닐 까 생각됩니다. 나온지도 오래 됐고, 처음부터 안드로이드에 적용하여 사용하다보니 안정성도 보장되어 있구요. 그런데 sqlite를 안드로이드에서 이용하기 위해서는 보일플레이트 코드가 많이 들어갑니다. Helper객체와 Provier객체가 필요하죠. 상황에 따라서는 Provider객체를 만들지 않고 사용할 수도 있지만요.

이번에 사용해보면서 나름 괜찮다고 생각되는 라이브러리입니다. Square에서 나온 Sqlbrite입니다. React programming을 할 수 있도록 도와줍니다.

sqlbrite git주소에 가면 dagger를 이용한 샘플이 존재합니다. 그런데 이 샘플은 square에서 내놓은 dagger1이죠. 

그래서 전 Dagger2를 이용한 샘플을 준비해 볼 까 합니다.

이전에 Dagger2를 이용한 개발 방법을 얘기 한 적이 있습니다. 

Android 개발에서 Dagger2이용해보기.

Dagger1과 Dagger2는 DI관점에서는 차이가 없지만, 사용방법에 대해서는 조금 차이가 있습니다. 그건 

http://google.github.io/dagger/dagger-1-migration.html

의 내용을 보면 알기 쉬울 듯 합니다.

아직 library버전이 v0.x대의 버전입니다. 사실 오픈소스 라이브러리를 v1.x대가 아니라면 사용하는데 꺼려지긴 합니다. 그래서 이걸 사용하는데 믿음직한 이유가 제이크 왓슨 형님과 스퀘어에서 내놨다는 이유이지 않을 까 합니다 하하.

샘플은 

https://github.com/drcarter/Dagger2Example

에 있습니다. Sqlbrite에 있는 샘플과 같은 내용이지만 단지 Dagger2로 변경한 부분입니다.

얼마전 제 블로그의 포스트에서 Dagger2를 이용하는 방법을 간단하게 적어보았습니다.

Android 개발에서 Dagger2이용해보기.

그런데 여기서 @Scope에 대한 설명이 없었던 것 같습니다.

@Scope ... 실질적으로는 Dagger2에서는 @Singleton annotation을 많이 사용합니다. Singleton은 굳이 자세히 설명을 안해도 다들 잘 알고 계시리라 봅니다. 객체 주입에서 @Singleton annotation을 적용해 두면.. 전체 App의 Scope안에서 Singleton객체가 생성되어 주입 됩니다. 그렇다면 다른 custom scope는 없을까요? 물론 @Scope annotation의 custom을 만들어서 사용합니다. 예를 들어서 Activity life cycle에서만 존재할 수 있는 객체 주입을 위해선 @PerActivity라던지 아니면 fragment life cycle에서만 객체 주입이 유지될 수 잇는 @PerFragment같은 custome scope annotation을 만들어 사용할 수 있습니다.



http://fernandocejas.com/wp-content/uploads/2015/04/composed_dagger_graph1.png


그럼 @Scope 를 이용한 예를 들어 볼 까 합니다.

이전에 예를 들었던 포스트인

Fragment에서 onActivityResult 결과 받기.

의 내용을 Dagger2를 이용한 방법으로 변경할려고 합니다. 주된 내용은 기존의 내용에서 EventBus객체를 Singleton pattern으로 생성해서 사용했습니다. 그럼 Dagger2를 이용해서 해당 샘플을 변경하면 어떻게 될까요?

우선 EventBus.java 입니다.

public class EventBus extends Bus {
private Handler handler = new Handler(Looper.getMainLooper());
@Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
handler.post(new Runnable() {
@Override
public void run() {
EventBus.super.post(event);
}
});
}
}
}

보시는 것과 같이 일반 객체와 별 다를 게 없습니다.


그럼 EventBusModule.java 입니다.

@Module
public class EventBusModule {
@Singleton
@Provides
EventBus provideEventBus() {
return new EventBus();
}
}

해당 모듈에서 EventBus를 제공해줄 때 @Singleton annotation이 붙었습니다. 이건 해당 객체를 Singleton으로 제공해 준다는 겁니다.


그리고 EventBusModule의 객체를 Inject를 하기 위한 Bridge역활을 해주는 ApplicationComponent.java 입니다.

@Singleton
@Component(
modules = {
ApplicationModule.class,
EventBusModule.class
}
)
public interface ApplicationComponent {
EventBus getEventBus();
}

해당 Component도 @Singletone으로 적용해두었습니다.


해당 Component자체도 Singleton scope에서 사용하겠다는 내용 입니다.


만약 Activity life cycle에서만 사용할 수 있는 scope를 만든다면

@Scope
@Retention(RUNTIME)
public @interface PerActivity {
}

로 만든다면 원하는 scope에다가 @PerActivity annotation을 적용하면 됩니다.


예제는..

https://github.com/drcarter/RetriveActivityResult 에서 retrive_activity_result_dagger 브랜치를 보시면 됩니다.

https://github.com/drcarter/RetriveActivityResult/tree/retrive_activity_result_dagger

Dagger2

url : http://google.github.io/dagger/

요즘 안드로이드 개발에서 Dependency Injection방법을 이용한 개발 방법이 이슈로 자리잡고 있는 듯 합니다. 더군다나 네임드 개발자로 알려진 Jake Wharton 형님이 발표한 자료도 있습니다.

https://speakerdeck.com/jakewharton/dependency-injection-with-dagger-2-devoxx-2014

2라는 숫자가 있는 것을 보면 Dagger1이 있다는 얘기가 됩니다. 처음 Dagger는 Square에서 나왔습니다. 

http://square.github.io/dagger/

이 사이트에 가시면 자세한 설명을 보실 수 있어요. 나중에 Dagger에 대한 샘플을 작성해 볼 생각입니다.


Dagger2를 얘기한 이유는 프로젝트가 google github에 있다는 것 때문입니다. 믿고 써보는 google??이라서?


Dagger2에서 중요한 Annotation은  @Module, @Provides, @Component, @Inject 정도 일 것 같습니다.

@Module : Module로 구성되고, 내부에서 주입될 객체들을 provide해주는 것.

@Provides : 객체 주입에 필요한 내용을 리턴해주는것

@Component : Module과 Inject간의 Bridge같은 역활.

@Inject : 객체의 주입.

대충 이런정도의 설명... 저는 참 설명을 못하네요 ㅡ.ㅡ


Dagger2를 이용하기 위한 전반적인 순서 입니다.

gradle 스크립트에 아래와 같은 내용을 추가 해 줍니다.

apply plugin: 'com.neenbedankt.android-apt'

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

android {
    ........

    dependencies {
        .......
        provided 'javax.annotation:jsr250-api:1.0'
        apt 'com.google.dagger:dagger-compiler:2.0'
        compile 'com.google.dagger:dagger:2.0'
    }
}


그리고 사용할 모델들을 정의 해 줍니다. 저는 아래와 같은 예제를 만들어 보았습니다.

Motor.java

public class Motor {

    private int speed;

    public Motor() {
        this.speed = 0;
    }

    public int getSpeed() {
        return speed;
    }

    public void accelerate(int value) {
        speed = speed + value;
    }

    public void setSpeed(int value) {
        speed = value;
    }

    public void brake() {
        speed = 0;
    }
}


Bike.java

public class Bike {

    private Motor motor;

    public Bike(@NonNull Motor motor) {
        this.motor = motor;
    }

    public void increaseSpped() {
        this.motor.setSpeed(this.motor.getSpeed() + 1);
    }

    public void decreaseSpeed() {
        if (this.motor.getSpeed() > 0) {
            this.motor.setSpeed(this.motor.getSpeed() - 1);
        }
    }

    public int getSpeed() {
        return this.motor.getSpeed();
    }
}


그리고 Module을 정의 합니다.

우선 MotorModule.java 입니다. 이건 단순히 Motor객체를  제공해 주는 역활만 합니다.

@Module
public class MotorModule {

    @Provides
    Motor provideMotor() {
        return new Motor();
    }
}


BikeModule.java입니다.

@Module(
        includes = MotorModule.class
)
public class BikeModule {

    @Provides
    Bike provideBike(Motor motor) {
        return new Bike(motor);
    }
}


여기서 보면 Bike객체를 제공해주는 @Provides을 보면 Motor객체가 파라미터로 존재합니다. 과연 이 파라미터는 어디서 제공받고 있을까 라는 의문이 듭니다. 이건 @Module의 includes부분에 보면 MotorModule.class를 포함하고 있고, MotorModule에서 Motor객체를 provides하고 있기에 가능합니다.

이렇게 되면 BikeModule안에서 Motor객체를 필요로 하는 곳에서는 모두 파라미터로 정의하면 Motor객체를 제공받을 수 있습니다.

그리고 Provides된 것을 Inject 하여 사용할 수 있는데 Bike객체를 Inject하여 사용해 볼까 합니다.

그럼 Bike는 어디에서 사용할 까요? 위에서 중요 Annotation중에 @Component를 알려드렸습니다. Module과 Inject간의 Brige같은 역활을 한다고 했는데 사용할 Component는 아래와 같습니다.

MainActivityComponent.java

@Component(
        ...
        modules = {
                ...
                BikeModule.class
        }
)
public interface MainActivityComponent extends ActivityComponent {
    void inject(@NonNull MainActivity mainActivity);
}


이와 같이 Component를 정의했습니다.

이렇게 Component를 정의하고 컴파일 하면 "DaggerMainActivityComponent가" 만들어 집니다. 이걸 가지고 실질적으로 객체 주입을 진행할 수 있죠.

DaggerMainActivityComponent.builder()
        ...
        .bikeModule(new BikeModule())
        .build();


이와 같이 하면 사용할 Component에 대해서는 준비가 끝납니다. 그럼 객체 주입은 어떻게 할까요?  몇가지 방법이 있지만 저는 객체에 @Inject anootation방법을 이용해서 사용 할려고 준비했습니다.


@Inject
Bike bike;

이와 같이 하여 Bike객체 사용에 대한 준비를 합니다.

이건 component에서 inejct라는 메소드를 정의해서 상용할 수 있는 부분입니다 그럼  inject라는 것을 하지 않고 사용할 방법은 있을까요? Component에서 getBike()라는 메소드를 만들어서 사용할 방법도 있긴 합니다.

이 예제에 대한 소스는

https://github.com/drcarter/Dagger2Example

에 올려둿습니다..

저도 Dagger2에 대해서 공부하고 정리하다 보니 설명이 명확하진 않은 듯 합니다.

오히려 맨 위에 Jake Wharton형님이 pt로 설명한 내용이 더 좋을지도요 ㅎㅎㅎ


+ Recent posts