Android projesinde DAGGER bağımlılık enjeksiyonunu sıfırdan nasıl kurabilirim?


100

Dagger nasıl kullanılır? Dagger'ı Android projemde çalışacak şekilde nasıl yapılandırırım?

Android projemde Dagger'ı kullanmak istiyorum ama kafa karıştırıcı buluyorum.

DÜZENLEME: Dagger2 de 2015 04 15'ten beri çıktı ve daha da kafa karıştırıcı!

[Bu soru, Dagger1 hakkında daha fazla şey öğrendikçe ve Dagger2 hakkında daha fazla şey öğrendikçe cevabıma eklediğim bir "taslak" dır. Bu soru bir "soru" dan ziyade bir rehber niteliğindedir .]



Bunu paylaştığın için teşekkürler ViewModel sınıflarını nasıl enjekte edeceğiniz konusunda bilginiz var mı? ViewModel sınıfım herhangi bir @AssistedInject içermiyor, ancak Dagger grafiği tarafından sağlanabilen bağımlılıkları var mı?
AndroidDev


Dagger2 ile bir soru daha: Bir nesneye sahip olmak mümkün mü ve referansı ViewModelve tarafından paylaşılıyor PageKeyedDataSourcemu? RxJava2 kullandığım ve CompositeDisposable'ın her iki sınıf tarafından paylaşılmasını istediğim gibi ve kullanıcı geri düğmesine basarsa Disposable nesneyi temizlemek istiyorum. Buraya vaka ekledim: stackoverflow.com/questions/62595956/…
AndroidDev

ViewModelCompositeDisposable'ı içine koymaktan daha iyi olursunuz ve özel PageKeyedDataSource'unuzun yapıcı argümanı olarak aynı compositeDisposable'ı iletebilirsiniz, ancak bu kısım için gerçekten Dagger'ı kullanmam çünkü alt kapsamdaki alt bileşenlere ihtiyacınız var ve Hilt bunu gerçekten desteklemeyecek senin için kolay.
EpicPandaForce

Yanıtlar:


193

Dagger 2.x Kılavuzu (Revize Edilmiş Baskı 6) :

Adımlar şu şekildedir:

1.) eklemek Daggeriçin için build.gradledosyaları:

  • üst düzey 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()
    }
}
  • uygulama düzeyinde 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 Bağımlılıkları sağlayan sınıfınızı oluşturun .

@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 enjekte edilebilir sınıfları almak için arayüz sağlayan sınıfı oluşturun .

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

3.1.) Bir uygulama ile bir modülü nasıl yaratırsınız:

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

Dikkat:: Oluşturulan bileşenin içinde kapsamlı bir sağlayıcı elde etmek için modülün açıklamalı yönteminde @Scopeek açıklama ( @Singletonveya gibi @ActivityScope) sağlamanız gerekir @Provides, aksi takdirde kapsamı kaldırılır ve her enjekte ettiğinizde yeni bir örnek alırsınız.

3.2.) Ne enjekte edebileceğinizi belirten Uygulama kapsamlı bir bileşen oluşturun (bu, injects={MainActivity.class}Dagger 1.x'teki ile aynıdır ):

@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.) Bunu bağımlılıkları için olabilir bir yapıcı aracılığıyla kendiniz oluşturup Kullanarak yeniden tanımlamak istemeyeceksiniz@Module , sen kullanabilirsiniz) örneğin, bunun yerine inşa tatlar kullanın (uygulama türünü değiştirmek için @Injectaçıklamalı yapıcısı.

public class Something {
    OtherThing otherThing;

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

Ayrıca, kullanırsanız @Inject yapıcı kullanıyorsanız, alan yerleştirmeyi açıkça çağırmak zorunda kalmadan kullanabilirsiniz component.inject(this):

public class Something {
    @Inject
    OtherThing otherThing;

    @Inject
    public Something() {
    }
}

Bu @Injectyapıcı sınıfları, bunları bir modülde açıkça belirtmek zorunda kalmadan aynı kapsamın bileşenine otomatik olarak eklenir.

Bir @Singletonkapsamlı @Injectyapıcı sınıfı görüleceği @Singletonkapsamlı bileşenler.

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

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

3.4.) Belirli bir arayüz için belirli bir uygulama tanımladıktan sonra, örneğin:

public interface Something {
    void doSomething();
}

@Singleton
public class SomethingImpl {
    @Inject
    AnotherThing anotherThing;

    @Inject
    public SomethingImpl() {
    }
}

Spesifik uygulamayı arayüze bir @Module.

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

Dagger 2.4'den beri bunun için kısa bir el aşağıdaki gibidir:

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

4.) birInjector uygulama düzeyindeki bileşeninizi işlemek için sınıf (monolitik bileşenin yerini alır ObjectGraph)

(not: APT kullanarak oluşturucu sınıfı Rebuild Projectoluşturmak için 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.) sizin oluşturmak CustomApplicationsınıf

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

6.) ekleyin CustomApplicationadresinden Müşteri AndroidManifest.xml.

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

7.) Sınıflarınızı enjekte edinMainActivity

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.) Keyfini çıkarın!

+1.) Etkinlik düzeyinde kapsamlı bileşenlerScope oluşturabileceğiniz bileşenlerinizi belirtebilirsiniz . Alt kapsamlar, tüm uygulama boyunca değil, yalnızca belirli bir alt kapsam için ihtiyaç duyduğunuz bağımlılıkları sağlamanıza olanak tanır. Tipik olarak, her Aktivite bu kurulumla kendi modülüne sahip olur. Lütfen kapsamlı bir sağlayıcının mevcut olduğunu unutmayın her bileşen , yani bu etkinliğin örneğini korumak için bileşenin kendisinin konfigürasyon değişikliğinden sağ çıkması gerekir. Örneğin, onRetainCustomNonConfigurationInstance()bir Harç dürbünüyle hayatta kalabilir .

Alt kapsam oluşturma hakkında daha fazla bilgi için Google kılavuzuna bakın . Ayrıca, sağlama yöntemleri hakkında bu siteye ve ayrıca bileşen bağımlılıkları bölümüne bakın ) ve buraya .

Özel bir kapsam oluşturmak için kapsam niteleyici ek açıklamasını belirtmelisiniz:

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

Bir alt kapsam oluşturmak için, bileşeninizdeki kapsamı belirtmeniz ApplicationComponentve bağımlılık olarak belirtmeniz gerekir . Açıkçası, alt kapsamı modül sağlayıcı yöntemlerinde de belirtmeniz gerekir.

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

    void inject(YourScopedClass scopedClass);
}

Ve

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

Bağımlılık olarak yalnızca bir kapsamlı bileşenin belirtilebileceğini lütfen unutmayın . Java'da çoklu kalıtımın desteklenmediği gibi düşünün.

+2.) Hakkında@Subcomponent : esasen, kapsamlı @Subcomponentbir bileşen bağımlılığının yerini alabilir; ancak açıklama işlemcisi tarafından sağlanan bir oluşturucu kullanmak yerine, bir bileşen fabrikası yöntemi kullanmanız gerekir.

Yani bu:

@Singleton
@Component
public interface ApplicationComponent {
}

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

    void inject(YourScopedClass scopedClass);
}

Şuna dönüşür:

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

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

Ve bu:

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

Şuna dönüşür:

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

+3.): Lütfen Dagger2 ile ilgili diğer Stack Overflow sorularını da kontrol edin, çok fazla bilgi sağlarlar. Örneğin, mevcut Dagger2 yapım bu cevapta belirtildi .

Teşekkürler

Github , TutsPlus , Joe Steele , Froger MCS ve Google'daki kılavuzlar için teşekkür ederiz .

Ayrıca bu yazıyı yazdıktan sonra bulduğum adım adım geçiş kılavuzu için.

Ve Kirill'in kapsam açıklaması için .

Resmi belgelerde daha fazla bilgi .


DaggerApplicationComponent
Thanasis Kapelonis

1
@ThanasisKapelonis DaggerApplicationComponent, derleme üzerinde APT tarafından otomatik olarak oluşturulur, ancak onu ekleyeceğim.
EpicPandaForce

1
CustomApplication paket kapsamının dışında olduğundan ve her şey mükemmel çalıştığından, Injector.initializeApplicationComponent yöntemini herkese açık hale getirmek zorunda kaldım! Teşekkürler!
Juan Saravia

2
Biraz geç ama belki aşağıdaki örnekler herkese yardımcı olabilir: github.com/dawidgdanski/android-compass-api github.com/dawidgdanski/Bakery
dawid gdanski

1
"Uyarı: Ek açıklama işleme için uyumsuz eklentiler kullanılıyor: android-apt. Bu, beklenmeyen bir davranışa neden olabilir. ' 1. adımda, apt 'com.google.dagger: dagger-compiler: 2.7' 'yi annotationProcessor' com.google.dagger: dagger-compiler: 2.7 'olarak değiştirin ve tüm apt config'i kaldırın. Ayrıntılar burada bulunabilir bitbucket.org/hvisser/android-apt/wiki/Migration
thanhbinh84

11

Hançer Kılavuzu 1.x :

Adımlar şu şekildedir:

1.) bağımlılıklar Daggeriçin build.gradledosyaya ekleyin

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

Ayrıca, packaging-optionbir hatayı önlemek için ekleyin duplicate APKs.

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

2.) bir oluşturma Injectorişlemek için sınıf 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.) RootModuleGelecekteki modüllerinizi birbirine bağlamak için bir oluşturun . Lütfen not alacağınız injectsher sınıfı belirtmek için eklemeniz gerektiğini unutmayın, @Injectaksi takdirde Dagger fırlatır RuntimeException.

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

4.) Modüllerinizde Root'ta belirtilen başka alt modülleriniz olması durumunda, bunlar için modüller oluşturun:

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

5.) Bağımlılıkları yapıcı parametreler olarak alan yaprak modülleri oluşturun. Benim durumumda döngüsel bir bağımlılık yoktu, bu yüzden Dagger'ın bunu çözüp çözemeyeceğini bilmiyorum, ama pek olası bulmuyorum. Yapıcı parametreleri ayrıca bir Module by Dagger içinde sağlanmalıdır, eğer belirtirseniz complete = falsediğer Modüllerde de olabilir.

@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.) genişletmek Applicationve başlatmak Injector.

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

7.) MainActivitySizde, onCreate()yöntemdeki Enjektörü arayın .

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

8.) @InjectSizin içinde kullanın MainActivity.

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

...

Hatayı no injectable constructor foundalırsanız, @Providesek açıklamaları unutmadığınızdan emin olun .


Bu cevap kısmen tarafından üretilen koda dayanmaktadır Android Bootstrap. Yani, anladıkları için onlara kredi verin. Çözüm kullanır Dagger v1.2.2.
EpicPandaForce

3
Kapsamı dagger-compileredilmelidir providedaksi takdirde uygulamaya dahil edilecektir ve bu GPL lisansı altında.
Denis Kniazhev

@deniskniazhev oh, bunu bilmiyordum! Söylediğin için teşekkürler!
EpicPandaForce
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.