brunch

You can make anything
by writing

C.S.Lewis

by 이승현 May 29. 2017

Dagger 2 #04

Dagger 2 & Android

Dagger 2 & Android


의존성 주입(DI)을 이용하여 안드로이드 앱을 만드는 데 가장 큰 어려움 중 하나는 Activity나 Fragment 같은 많은 안드로이드 프레임워크 클래스들이 OS에 의해 인스턴스화 된다는 점입니다.

하지만 Dagger는 이러한 안드로이드 프레임워크 클래스들도 주입할 수 있습니다.


구글링을 해보면 예전에 만들어진 예제들이 많은데, 가이드에서는 공식적인 dagger.android을 이용하기 권장합니다.




#01 Simple logic


Activity 인스턴스와 SharedPreferenceManager 인스턴스를 Dagger를 이용해 주입하는 예제를 dagger.android API를 이용해 만들었습니다.




#01 build.gradle


android {
    ...
    android {
        configurations.all {
            resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
        }
    }
}

dependencies {
    ...
    // dagger
    compile 'com.google.dagger:dagger:2.11-rc2'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
    provided 'javax.annotation:jsr250-api:1.0'

    // dagger android
    compile 'com.google.dagger:dagger-android:2.11-rc2'
    compile 'com.google.dagger:dagger-android-support:2.11-rc2'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.11-rc2'
}


최신 릴리즈 정보는 github에서 확인할 수 있습니다.

https://github.com/google/dagger/releases




#02 의존성 선언


#02 Inject Annotation


먼저 의존성을 요청할 @Inject annotation을 선언합니다.

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import javax.inject.Inject;

import shlee.myapplication.android.SharedPreferenceManager;

public class MainActivity extends AppCompatActivity {

    @Inject
    SharedPreferenceManager sharedPreferenceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sharedPreferenceManager.setName("dagger test");
        String name = sharedPreferenceManager.getName();
    }

}


import android.content.Context;
import android.content.SharedPreferences;

import javax.inject.Inject;
import javax.inject.Singleton;

import shlee.myapplication.MainActivity;

@Singleton // Application scope
public class SharedPreferenceManager {

    private SharedPreferences sharedPreferences;

    @Inject
    SharedPreferenceManager(MainActivity activity) {
        sharedPreferences = activity.getSharedPreferences("demo", Context.MODE_PRIVATE);
    }

    public void setName(String name) {
        sharedPreferences.edit().putString("Name", name).apply();
    }

    public String getName() {
        return sharedPreferences.getString("Name", "default");
    }

}


@Inject annotation에 대한 좀 더 자세한 설명은 이전 글에 있습니다.




#03 의존성 충족


#03 Provides, Module Annotation with javaProvides, Module Annotation


이전 글에서는 Module에서 @Provide 메서드를 통해 어떻게 의존성을 구성하고 제공하는지를 정의했었습니다. 구글링 해보면 Activity 클래스도 @Provide 메서드를 통해 정의할 수 있지만, 앞서 기재했듯이 dagger.android API를 이용하여 만들어 보겠습니다.




#03-01 AndroidInjector<MainActivity>


#04 Subcomponent annotation at AndroidInjctor<MainActivity>


먼저 MainActivity를 주입하기 위해 AndroidInjector<MainActivity>를 구현하는 @Subcomponent를 작성하고, AndroidInjector.Builder<MainActivity>를 상속받는 @Subcomponent.Builder를 작성합니다.

AndroidInjector<T> 간단히 말하면 안드로이드 프레임워크 클래스들(Application, Activity, Fragment, Service, BroadcastReceiver, ContentProvider)을 주입시켜주는 클래스입니다.


import javax.inject.Singleton;

import dagger.Subcomponent;
import dagger.android.AndroidInjector;
import shlee.myapplication.MainActivity;

@Singleton // SharedPreferenceManager is @Singleton class.
@Subcomponent
public interface MainActivityComponent extends AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {}

}




#03-02 AndroidInjector<App>


#05 Component annotation at AndroidInjector<App>


MainActivity를 주입하기 위해 몇 가지 구현 요소가 더 필요합니다.

android.app.Application을 상속받는 App 클래스 구현

App을 주입하기 위해 AndroidInjector<App>을 구현하는 @Component를 작성하고 AndroidInjector.Builder <App>을 상속받는 @Component.Builder를 작성합니다.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="shlee.myapplication">

    <application
        android:name=".android.App"
        android:allowBackup="true"
        ....
   </application>

</manifest>
import android.app.Application;

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }


}


import dagger.Component;
import dagger.android.AndroidInjector;

@Component(modules = {Module.class})
interface AppComponent extends AndroidInjector<App>{

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<App> {}

}




#03-03 Module


#06 Module annotation


Module을 작성할 때 subcomponent인 MainActivityComponent를 더하고, MainActivityComponent.Builder에 바인드 합니다.



import android.app.Activity;
import dagger.Binds;
import dagger.android.ActivityKey;
import dagger.android.AndroidInjector;
import dagger.multibindings.IntoMap;
import shlee.myapplication.MainActivity;

@dagger.Module(subcomponents = MainActivityComponent.class)
abstract class Module {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindMainActivityInjectorFactory(MainActivityComponent.Builder builder);

}
import dagger.Component;
import dagger.android.AndroidInjector;

@Component(modules = {Module.class})
interface AppComponent extends AndroidInjector<App>{

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<App> {}

}




#04 DaggerAppComponent


#07 Implements DaggerAppComponent


@Inject, @Module, @Component를 모두 구성하고 build를 하면 AppComponent를 implement한 DaggerAppComponent가 자동 생성됩니다.

DaggerAppComponent를 build/create 하기 전에 몇 가지 작업이 더 필요합니다.





App 클래스에 HasActivityInjector interface를 implement 하고 activityInjector() 메서드에서 return 하기 위해 DispatchingAndroidInjector<Activity>를 @Inject 합니다.

MainActivity 클래스의 onCreate() 메서드에서 super.onCreate(savedInstanceState) 이전에 AndroidInjection.inject(this)를 호출합니다.


import android.app.Activity;
import android.app.Application;

import javax.inject.Inject;

import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasActivityInjector;

public class App extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.builder().create(this).inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

}
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import javax.inject.Inject;

import dagger.android.AndroidInjection;
import shlee.myapplication.android.SharedPreferenceManager;

public class MainActivity extends AppCompatActivity {

    @Inject
    SharedPreferenceManager sharedPreferenceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sharedPreferenceManager.setName("dagger test");
        String name = sharedPreferenceManager.getName();
    }

}





마지막으로 DaggerAppComponent를 create 하고 App을 inject 합니다.

실제로 의존성(MainActivity, SharedPreferenceManager 인스턴스)를 주입하는 부분은 Dagger가 담당하게 됩니다.




https://google.github.io/dagger//android.html




Dagger2가 아직은 Beta 단계인 API가 많습니다.

그러다 보니 구글링을 해봐도 최신 API를 이용한 예제가 없네요.

공식 가이드 문서나, github에도 설명이 부족합니다.


무엇보다도 쓰기 굉장히 복잡합니다ㅠ 특히 안드로이드 주요 구성 요소들과 결합하면 더 복잡해지네요...

Activity 하나를 주입시키기 위해 만들어야 하는 클래스, 인스턴스와 관련된 내용들을 이해하려면 쉽지 않습니다.


우선 간단한 부분부터 Dagger를 이용하고 API의 버전이 올라가서 직관성이 좋아지면 더 많이 쓸 계획입니다.

작가의 이전글 Dagger 2 #03

작품 선택

키워드 선택 0 / 3 0

댓글여부

afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari