Android 프로젝트에서 처음부터 DAGGER 종속성 주입을 설정하는 방법은 무엇입니까?


100

Dagger를 사용하는 방법? Android 프로젝트에서 작동하도록 Dagger를 구성하는 방법은 무엇입니까?

Android 프로젝트에서 Dagger를 사용하고 싶지만 혼란 스럽습니다.

편집 : Dagger2는 2015 년 04 월 15 일 이후로 출시되었으며 훨씬 더 혼란 스럽습니다!

[이 질문은 내가 Dagger1에 대해 더 많이 배우고 Dagger2에 대해 더 많이 배우면서 내 대답에 추가하는 "스텁"입니다. 이 질문은 "질문"이라기보다는 가이드에 가깝습니다.]



공유해 주셔서 감사합니다. ViewModel 클래스를 주입하는 방법에 대한 지식이 있습니까? 내 ViewModel 클래스에는 @AssistedInject가 없지만 Dagger 그래프에서 제공 할 수있는 종속성이 있습니까?
AndroidDev


하나 개 더 질문으로 Dagger2, 그것은 개체를 가질 수 및 IT의 참조는이 공유 ViewModel하고 PageKeyedDataSource? RxJava2를 사용하고 CompositeDisposable을 두 클래스에서 공유하고 사용자가 뒤로 버튼을 누르면 Disposable 객체를 지우고 싶습니다. 여기에 사례를 추가했습니다 : stackoverflow.com/questions/62595956/…
AndroidDev

CompositeDisposable을 내부에 넣고 ViewModel사용자 정의 PageKeyedDataSource의 생성자 인수로 동일한 compositeDisposable을 전달하는 것이 좋지만 하위 범위 하위 구성 요소가 필요하기 때문에 해당 부분에 Dagger를 실제로 사용하지 않을 것입니다. Hilt는 실제로 지원하지 않습니다. 당신을 위해 쉽습니다.
EpicPandaForce

답변:


193

Dagger 2.x 가이드 (개정판 6) :

단계는 다음과 같습니다.

1) 추가 Dagger귀하에 build.gradle파일 :

  • 최상위 build.gradle :

.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //added apt for source code generation
    }
}

allprojects {
    repositories {
        jcenter()
    }
}
  • 앱 수준 build.gradle :

.

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt' //needed for source code generation

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"

    defaultConfig {
        applicationId "your.app.id"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    apt 'com.google.dagger:dagger-compiler:2.7' //needed for source code generation
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.google.dagger:dagger:2.7' //dagger itself
    provided 'org.glassfish:javax.annotation:10.0-b28' //needed to resolve compilation errors, thanks to tutplus.org for finding the dependency
}

2.)AppContextModule 종속성을 제공하는 클래스를 만듭니다 .

@Module //a module could also include other modules
public class AppContextModule {
    private final CustomApplication application;

    public AppContextModule(CustomApplication application) {
        this.application = application;
    }

    @Provides
    public CustomApplication application() {
        return this.application;
    }

    @Provides 
    public Context applicationContext() {
        return this.application;
    }

    @Provides
    public LocationManager locationService(Context context) {
        return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    }
}

3.)AppContextComponent 주입 가능한 클래스를 얻기 위해 인터페이스를 제공 하는 클래스를 만듭니다 .

public interface AppContextComponent {
    CustomApplication application(); //provision method
    Context applicationContext(); //provision method
    LocationManager locationManager(); //provision method
}

3.1.) 다음은 구현으로 모듈을 만드는 방법입니다.

@Module //this is to show that you can include modules to one another
public class AnotherModule {
    @Provides
    @Singleton
    public AnotherClass anotherClass() {
        return new AnotherClassImpl();
    }
}

@Module(includes=AnotherModule.class) //this is to show that you can include modules to one another
public class OtherModule {
    @Provides
    @Singleton
    public OtherClass otherClass(AnotherClass anotherClass) {
        return new OtherClassImpl(anotherClass);
    }
}

public interface AnotherComponent {
    AnotherClass anotherClass();
}

public interface OtherComponent extends AnotherComponent {
    OtherClass otherClass();
}

@Component(modules={OtherModule.class})
@Singleton
public interface ApplicationComponent extends OtherComponent {
    void inject(MainActivity mainActivity);
}

주의 : : 생성 된 구성 요소 내에서 범위가 지정된 공급자를 가져 오려면 모듈의 주석 처리 된 메서드 에 @Scope주석 ( @Singleton또는 @ActivityScope) 을 제공해야합니다 @Provides. 그렇지 않으면 범위가 지정되지 않으며 삽입 할 때마다 새 인스턴스를 받게됩니다.

3.2.) 주입 할 수있는 항목을 지정하는 응용 프로그램 범위 구성 요소를 만듭니다 ( injects={MainActivity.class}Dagger 1.x 의 경우와 동일 ).

@Singleton
@Component(module={AppContextModule.class}) //this is where you would add additional modules, and a dependency if you want to subscope
public interface ApplicationComponent extends AppContextComponent { //extend to have the provision methods
    void inject(MainActivity mainActivity);
}

3.3.) 생성자를 통해 직접 생성 할 있고를 사용하여 재정의하고 싶지 않은 종속성의 경우 @Module(예 : 구현 유형을 변경하기 위해 빌드 플레이버를 사용하는 경우) @Inject주석이있는 생성자를 사용할 수 있습니다 .

public class Something {
    OtherThing otherThing;

    @Inject
    public Something(OtherThing otherThing) {
        this.otherThing = otherThing;
    }
}

또한 @Inject생성자 를 사용 하면 명시 적으로 호출하지 않고도 필드 주입을 사용할 수 있습니다 component.inject(this).

public class Something {
    @Inject
    OtherThing otherThing;

    @Inject
    public Something() {
    }
}

이러한 @Inject생성자 클래스는 모듈에서 명시 적으로 지정할 필요없이 동일한 범위의 구성 요소에 자동으로 추가됩니다.

@Singleton범위 @Inject생성자 클래스에서 볼 수됩니다 @Singleton범위의 구성 요소.

@Singleton // scoping
public class Something {
    OtherThing otherThing;

    @Inject
    public Something(OtherThing otherThing) {
        this.otherThing = otherThing;
    }
}

3.4.) 주어진 인터페이스에 대한 특정 구현을 정의한 후 다음과 같이하십시오.

public interface Something {
    void doSomething();
}

@Singleton
public class SomethingImpl {
    @Inject
    AnotherThing anotherThing;

    @Inject
    public SomethingImpl() {
    }
}

.NET Framework를 사용하여 인터페이스에 특정 구현을 "바인딩"해야합니다 @Module.

@Module
public class SomethingModule {
    @Provides
    Something something(SomethingImpl something) {
        return something;
    }
}

Dagger 2.4 이후의 약어는 다음과 같습니다.

@Module
public abstract class SomethingModule {
    @Binds
    abstract Something something(SomethingImpl something);
}

4.)Injector 애플리케이션 수준 구성 요소를 처리 할 클래스를 만듭니다 (모 놀리 식을 대체 함 ObjectGraph).

(참고 : APT Rebuild Project를 사용하여 DaggerApplicationComponent빌더 클래스 생성 )

public enum Injector {
    INSTANCE;

    ApplicationComponent applicationComponent;

    private Injector(){
    }

    static void initialize(CustomApplication customApplication) {
        ApplicationComponent applicationComponent = DaggerApplicationComponent.builder()
           .appContextModule(new AppContextModule(customApplication))
           .build();
        INSTANCE.applicationComponent = applicationComponent;
    }

    public static ApplicationComponent get() {
        return INSTANCE.applicationComponent;
    }
}

5.)CustomApplication 수업 만들기

public class CustomApplication
        extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Injector.initialize(this);
    }
}

6) 추가 CustomApplication귀하에 AndroidManifest.xml.

<application
    android:name=".CustomApplication"
    ...

7.) 수업을MainActivity

public class MainActivity
        extends AppCompatActivity {
    @Inject
    CustomApplication customApplication;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Injector.get().inject(this);
        //customApplication is injected from component
    }
}

8.) 즐기십시오!

+1.) 활동 수준 범위 구성 요소를Scope 만들 수있는 구성 요소를 지정할 수 있습니다 . 하위 범위를 사용하면 전체 응용 프로그램이 아닌 지정된 하위 범위에 대해서만 필요한 종속성을 제공 할 수 있습니다. 일반적으로 각 활동은이 설정으로 자체 모듈을 가져옵니다. 범위가 지정된 공급자 는 구성 요소별로 존재합니다 . 즉, 해당 활동에 대한 인스턴스를 유지하려면 구성 요소 자체가 구성 변경을 유지해야합니다. 예를 들어, 또는 모르타르 범위를 통해 생존 할 수 있습니다.onRetainCustomNonConfigurationInstance()

하위 범위 지정에 대한 자세한 내용 은 Google 가이드를 확인하세요 . 또한 제공 방법구성 요소 종속성 섹션 에 대한이 사이트여기를 참조하십시오 .

사용자 지정 범위를 만들려면 범위 한정자 주석을 지정해야합니다.

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}

하위 범위를 만들려면 구성 요소에 범위를 지정 ApplicationComponent하고 종속성으로 지정해야 합니다. 분명히 모듈 공급자 메서드에도 하위 범위를 지정해야합니다.

@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
        extends ApplicationComponent {
    CustomScopeClass customScopeClass();

    void inject(YourScopedClass scopedClass);
}

@Module
public class CustomScopeModule {
    @Provides
    @YourCustomScope
    public CustomScopeClass customScopeClass() {
        return new CustomScopeClassImpl();
    }
}

하나의 범위 구성 요소 만 종속성으로 지정할 수 있습니다. Java에서 다중 상속이 지원되지 않는 것과 똑같이 생각하십시오.

+2.) 정보 @Subcomponent: 본질적으로 범위 @Subcomponent는 구성 요소 종속성을 대체 할 수 있습니다. 그러나 주석 프로세서가 제공하는 빌더를 사용하는 대신 컴포넌트 팩토리 메소드를 사용해야합니다.

그래서 이거:

@Singleton
@Component
public interface ApplicationComponent {
}

@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
        extends ApplicationComponent {
    CustomScopeClass customScopeClass();

    void inject(YourScopedClass scopedClass);
}

이렇게됩니다 :

@Singleton
@Component
public interface ApplicationComponent {
    YourCustomScopedComponent newYourCustomScopedComponent(CustomScopeModule customScopeModule);
}

@Subcomponent(modules={CustomScopeModule.class})
@YourCustomScope
public interface YourCustomScopedComponent {
    CustomScopeClass customScopeClass();
}

이:

DaggerYourCustomScopedComponent.builder()
      .applicationComponent(Injector.get())
      .customScopeModule(new CustomScopeModule())
      .build();

이렇게됩니다 :

Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule());

+3.) : Dagger2에 관한 다른 Stack Overflow 질문도 확인하세요. 많은 정보를 제공합니다. 예를 들어, 현재 Dagger2 구조 가이 답변에 지정되어 있습니다.

감사

Github , TutsPlus , Joe Steele , Froger MCSGoogle 의 가이드에 감사드립니다 .

또한 이 게시물을 작성한 후이 단계별 마이그레이션 가이드를 찾았습니다.

그리고 Kirill의 범위 설명 을 위해 .

공식 문서 에 더 많은 정보가 있습니다 .


DaggerApplicationComponent의 구현이 누락되었다고 생각합니다
Thanasis Kapelonis

1
@ThanasisKapelonis DaggerApplicationComponent는 빌드시 APT에 의해 자동 생성되지만 추가하겠습니다.
EpicPandaForce

1
내 CustomApplication이 패키지 범위 밖에 있고 모든 것이 완벽하게 작동하기 때문에 Injector.initializeApplicationComponent 메서드를 공개해야했습니다! 감사!
Juan Saravia

2
A는 후반 비트하지만 어쩌면 다음의 예는 사람이 도움이 될 것입니다 github.com/dawidgdanski/android-compass-api github.com/dawidgdanski/Bakery
DAWID GDANSKI

1
'경고 : 주석 처리에 호환되지 않는 플러그인 사용 : android-apt. 이로 인해 예기치 않은 동작이 발생할 수 있습니다. ' 1 단계에서 apt 'com.google.dagger : dagger-compiler : 2.7'을 annotationProcessor 'com.google.dagger : dagger-compiler : 2.7'로 변경하고 모든 적절한 구성을 제거합니다. 세부 사항은 여기에서 볼 수 있습니다 bitbucket.org/hvisser/android-apt/wiki/Migration
thanhbinh84

11

Dagger 1.x 가이드 :

단계는 다음과 같습니다.

1.) 종속성 Dagger을 위해 build.gradle파일에 추가

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    ...
    compile 'com.squareup.dagger:dagger:1.2.2'
    provided 'com.squareup.dagger:dagger-compiler:1.2.2'

또한에 packaging-option대한 오류를 방지하려면 추가하십시오 duplicate APKs.

android {
    ...
    packagingOptions {
        // Exclude file to avoid
        // Error: Duplicate files during packaging of APK
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }
}

2)을 생성 Injector을 처리하는 클래스 ObjectGraph.

public enum Injector
{
    INSTANCE;

    private ObjectGraph objectGraph = null;

    public void init(final Object rootModule)
    {

        if(objectGraph == null)
        {
            objectGraph = ObjectGraph.create(rootModule);
        }
        else
        {
            objectGraph = objectGraph.plus(rootModule);
        }

        // Inject statics
        objectGraph.injectStatics();

    }

    public void init(final Object rootModule, final Object target)
    {
        init(rootModule);
        inject(target);
    }

    public void inject(final Object target)
    {
        objectGraph.inject(target);
    }

    public <T> T resolve(Class<T> type)
    {
        return objectGraph.get(type);
    }
}

3.) RootModule미래 모듈을 함께 연결하기 위해 를 만듭니다 . 주석 injects을 사용할 모든 클래스를 지정 하려면 포함해야합니다 . @Inject그렇지 않으면 Dagger가 RuntimeException.

@Module(
    includes = {
        UtilsModule.class,
        NetworkingModule.class
    },
    injects = {
        MainActivity.class
    }
)
public class RootModule
{
}

4.) 루트에 지정된 모듈 내에 다른 하위 모듈이있는 경우 해당 모듈을 만듭니다.

@Module(
    includes = {
        SerializerModule.class,
        CertUtilModule.class
    }
)
public class UtilsModule
{
}

5.) 생성자 매개 변수로 종속성을받는 리프 모듈을 만듭니다. 제 경우에는 순환 종속성이 없었기 때문에 Dagger가이를 해결할 수 있는지는 모르겠지만 가능성은 낮습니다. 생성자 매개 변수는 Dagger에 의해 모듈에 제공되어야합니다. 지정하면 complete = false다른 모듈에도있을 수 있습니다.

@Module(complete = false, library = true)
public class NetworkingModule
{
    @Provides
    public ClientAuthAuthenticator providesClientAuthAuthenticator()
    {
        return new ClientAuthAuthenticator();
    }

    @Provides
    public ClientCertWebRequestor providesClientCertWebRequestor(ClientAuthAuthenticator clientAuthAuthenticator)
    {
        return new ClientCertWebRequestor(clientAuthAuthenticator);
    }

    @Provides
    public ServerCommunicator providesServerCommunicator(ClientCertWebRequestor clientCertWebRequestor)
    {
        return new ServerCommunicator(clientCertWebRequestor);
    }
}

6) 확장 Application및 초기화 Injector.

@Override
public void onCreate()
{
    super.onCreate();
    Injector.INSTANCE.init(new RootModule());
}

7.) MainActivity에서 onCreate()메서드 의 인젝터를 호출합니다 .

@Override
protected void onCreate(Bundle savedInstanceState)
{
    Injector.INSTANCE.inject(this);
    super.onCreate(savedInstanceState);
    ...

8)를 사용 @Inject하여에서 MainActivity.

public class MainActivity extends ActionBarActivity
{  
    @Inject
    public ServerCommunicator serverCommunicator;

...

오류가 발생 no injectable constructor found하면 @Provides주석을 잊지 않았는지 확인하십시오 .


이 답변은 부분적으로에서 생성 한 코드를 기반으로합니다 Android Bootstrap. 그래서, 그것을 알아 낸 것에 대한 크레딧. 솔루션은 Dagger v1.2.2.
EpicPandaForce 2014

3
범위는 그렇지 않으면 응용 프로그램에 포함 dagger-compiler되어야하며 providedGPL 라이선스에 따릅니다.
Denis Kniazhev 2015 년

@deniskniazhev 오, 나는 그것을 몰랐습니다! 알림 주셔서 감사합니다!
EpicPandaForce 2015-06-10
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.