좋은 프로그램은 마음의 여유에서 나온다.

dagger2 with realm 예제 본문

프로그래밍/안드로이드

dagger2 with realm 예제

좋은데이 2016. 8. 16. 16:31

dagger2로 realm 을 의존성 주입하는 예제


의존성 주입에 대한 설명은 위키피디아를 참조하시면 됩니다.


dagger2은 square에서 만든 dagger1을 구글이 계속 만들면서 현재는 2.6버전까지 나왔습니다.


dagger2는 정적이며 컴파일 타임에 디펜던시 인젝션을 제공하는 자바 프레임워크입니다.


dagger2에서 중요하다고 생각되는 어노테이션은 다음과 같습니다.


@Module : 주입된 객체들을 제공하는 메소드들을 모아놓은 클래스입니다. @Provide로 정의된 메소드에서 주입된 객체들을 어떻게 생성할지 정의하고 있습니다.


@Provide : @Module 어노테이션이 포함된 클래스에 정의되며, 의존성을 주입해줄 객체를 제공할 메소드를 정의해주는데 사용합니다.


@Component : 이 어노테이션은 @Module에 정의된 객체들과 의존성 주입을 받을 @Inject 객체들 사이를 연결해주는 역할을 합니다. interface에만 선언해야 하며, 의존성 주입을 받을 객체들을 포함한 클래스를 inject(클래스) 형태로 정의해주며, @Inject를 통해 의존성 주입을 받을 모든 객체들이 정의된 @Module을 포함해야 합니다. 컴파일시에 이 인터페이스는 DaggerXXX 형태의 클래스로 생성되게 됩니다.


@Inject : 이 어노테이션이 달린 클래스나 멤버 변수는 해당 객체를 dagger에게 요청하게 됩니다. dagger에 의해 의존성이 주입되게 됩니다.


기타 객체의 생명주기를 정의해주는 것(@Singleton 등)이나 주입할 객체를 명시적으로 지정해주는 어노테이션(@Qualifier) 등이 있습니다.


gradle 디펜던시 추가


현재 최신 버전은 2.6이다. 버터나이프와 함께 사용할 경우 2.5 이상 버전을 사용해야 합니다. 그 아래 버전에서는 에러 발생. (해당 이슈 : https://github.com/JakeWharton/butterknife/issues/686)



프로젝트 레벨 build.gradle


buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        classpath "io.realm:realm-gradle-plugin:1.1.0"
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


모듈 레벨 app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
// realm plugin
apply plugin: 'realm-android'

android {
    // your setting
    ....
}

dependencies {
    // your dependencies
    ....

    // dagger2 dependencies
    provided 'javax.annotation:jsr250-api:1.0'
    compile 'com.google.dagger:dagger:2.6'
    apt 'com.google.dagger:dagger-compiler:2.6'
}


의존성 주입이 될 객체들을 제공하는 메소드들을 모아놓은 모듈 클래스 정의 (이 예제에서는 Realm 인스턴스 제공)



@Module
public class ApplicationModule {

    @Provides
    public Realm provideRealm() {
        return Realm.getDefaultInstance();
    }
}


의존성 주입이 될 객체(@Module)들과 의존성 주입을 받을 대상(@Inject)들을 연결해주는 컴포넌트 클래스 정의

주의할 것은 inject() 함수를 만들때 실제 @Inject가 있는 class를 인자로 받아야 합니다.

MainActivity는 Activity를 상속받은 것이기 때문에 추상화로 모든 액티비티에 공통으로 쓰려고 Activity를 인자로 넣었더니

정상 동작하지 않아 한참 헤맸습니다....


@Singleton
@Component(modules = {
        ApplicationModule.class
})
public interface ApplicationComponent {

    void inject(MyApplication app);
    void inject(MainActivity activity);
}


커스텀 Application 클래스를 작성하여 manifest에 등록해 줍니다.



public class MyApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); // realm 설정 initRealmConfiguration(); // dagger application component 설정 initApplicationComponent(); } private void initRealmConfiguration() { RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this) .deleteRealmIfMigrationNeeded() .build(); Realm.setDefaultConfiguration(realmConfiguration); } private void initApplicationComponent() { this.applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } public ApplicationComponent getApplicationComponent() { return this.applicationComponent; } @Override public void onTerminate() { super.onTerminate(); } }


커스텀 Application에서 DaggerApplicationComponent 부분에서 에러가 발생하게 된다. 안드로이드 스튜디오에서 Build > Clean Project를 실행한 후 Build > Rebuild Project 를 실행하면 DaggerApplicationComponent가 생성되게 됩니다. 생성되는 클래스의 이름 규칙은 컴포넌트 어노테이션이 달린 interface 앞에 Dagger이 붙어 클래스가 생성됩니다.


생성된 클래스를 한번 열어보면 다음과 같이 위에서 만든 ApplicationComponent 인터페이스가 구현되어 있습니다.



@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class DaggerApplicationComponent implements ApplicationComponent {
  private Provider provideRealmProvider;

  private MembersInjector baseActivityMembersInjector;

  private DaggerApplicationComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideRealmProvider =
        ApplicationModule_ProvideRealmFactory.create(builder.applicationModule);

    this.baseActivityMembersInjector = BaseActivity_MembersInjector.create(provideRealmProvider);
  }

  @Override
  public void inject(MyApplication app) {
    MembersInjectors.noOp().injectMembers(app);
  }

  @Override
  public void inject(BaseActivity baseActivity) {
    baseActivityMembersInjector.injectMembers(baseActivity);
  }

  @Override
  public void inject(NMapActivity nMapActivity) {
    MembersInjectors.noOp().injectMembers(nMapActivity);
  }

  public static final class Builder {
    private ApplicationModule applicationModule;

    private Builder() {}

    public ApplicationComponent build() {
      if (applicationModule == null) {
        throw new IllegalStateException(
            ApplicationModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerApplicationComponent(this);
    }

    public Builder applicationModule(ApplicationModule applicationModule) {
      this.applicationModule = Preconditions.checkNotNull(applicationModule);
      return this;
    }
  }
}


마지막으로 객체를 주입 받을 액티비티이다. @Inject라고 선언된 Realm 의 객체는 @Module에 정의된 provideRealm()으로부터 주입받게 됩니다.



public class MainActivity extends AppCompatActivity { @Inject Realm mRealm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()) .getApplicationComponent() .inject(this); } }


* 빌드시 아래와 같은 에러가 나면 rx 패키지에 Observable 클래스를 추가하시면 됩니다.

* 참고 링크 : https://realm.io/docs/java/latest/#jackson-databind

java.lang.NoClassDefFoundError: rx.Observable ...


이 예제 코드는 깃허브에서 볼 수 있습니다.

Comments