Bagaimana cara mengatur injeksi ketergantungan DAGGER dari awal di proyek Android?

100

Bagaimana cara menggunakan Dagger? Bagaimana cara mengkonfigurasi Dagger agar berfungsi di proyek Android saya?

Saya ingin menggunakan Dagger dalam proyek Android saya, tetapi menurut saya itu membingungkan.

EDIT: Dagger2 juga keluar sejak 2015 04 15, dan itu bahkan lebih membingungkan!

[Pertanyaan ini adalah "rintisan" yang akan saya tambahkan ke jawaban saya saat saya mempelajari lebih lanjut tentang Dagger1, dan mempelajari lebih lanjut tentang Dagger2. Pertanyaan ini lebih merupakan panduan daripada "pertanyaan".]

EpicPandaForce
sumber
Juga lihat: stackoverflow.com/a/40546157/2413303
EpicPandaForce
Terima kasih telah membagikan ini. Apakah Anda memiliki pengetahuan tentang cara memasukkan kelas ViewModel? Kelas ViewModel saya tanpa @AssistedInject tetapi memiliki dependensi yang dapat disediakan oleh grafik Dagger?
AndroidDev
1
Oke, lihat stackoverflow.com/questions/60884402/…
EpicPandaForce
Satu pertanyaan lagi, Dengan Dagger2, Apakah mungkin memiliki objek dan referensinya dibagikan oleh ViewModeldan PageKeyedDataSource? Seperti saya menggunakan RxJava2, dan ingin CompositeDisposable dibagikan oleh kedua kelas dan jika pengguna menekan tombol kembali, saya ingin menghapus objek Disposable. Saya telah menambahkan kasus di sini: stackoverflow.com/questions/62595956/…
AndroidDev
Anda lebih baik meletakkan compositeDisposable di dalam ViewModeldan mungkin meneruskan compositeDisposable yang sama sebagai argumen konstruktor dari PageKeyedDataSource kustom Anda, tetapi saya tidak akan benar-benar menggunakan Dagger untuk bagian itu karena Anda memerlukan subkomponen berlangganan, dan Hilt tidak akan benar-benar mendukungnya. mudah untukmu.
EpicPandaForce

Jawaban:

193

Panduan untuk Dagger 2.x (Edisi Revisi 6) :

Langkah-langkahnya adalah sebagai berikut:

1.) tambahkan Daggerke build.gradlefile Anda :

  • tingkat atas 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()
    }
}
  • tingkat aplikasi 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.) Buat AppContextModulekelas Anda yang menyediakan dependensi.

@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.) membuat AppContextComponentkelas yang menyediakan antarmuka untuk mendapatkan kelas yang dapat diinjeksi.

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

3.1.) Beginilah cara Anda membuat modul dengan implementasi:

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

Hati-hati:: Anda perlu memberikan @Scopeanotasi (suka @Singletonatau @ActivityScope) pada @Providesmetode anotasi modul untuk mendapatkan penyedia yang tercakup dalam komponen yang Anda buat, jika tidak maka akan tidak tercakup, dan Anda akan mendapatkan instance baru setiap kali Anda memasukkan.

3.2.) Buat komponen Cakupan aplikasi yang menentukan apa yang dapat Anda masukkan (ini sama dengan yang ada injects={MainActivity.class}di 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.) Untuk dependensi yang dapat Anda buat sendiri melalui konstruktor dan tidak ingin didefinisikan ulang menggunakan a @Module(misalnya, Anda menggunakan ragam build sebagai gantinya untuk mengubah jenis implementasi), Anda dapat menggunakan @Injectkonstruktor beranotasi.

public class Something {
    OtherThing otherThing;

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

Selain itu, jika Anda menggunakan @Injectkonstruktor, Anda dapat menggunakan injeksi bidang tanpa harus memanggil secara eksplisit component.inject(this):

public class Something {
    @Inject
    OtherThing otherThing;

    @Inject
    public Something() {
    }
}

Ini @Injectkelas konstruktor secara otomatis ditambahkan ke komponen lingkup yang sama tanpa harus secara eksplisit menentukan mereka dalam modul.

Sebuah @Singletonscoped @Injectkelas konstruktor akan terlihat dalam @Singletonkomponen scoped.

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

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

3.4.) Setelah Anda menentukan implementasi khusus untuk antarmuka tertentu, seperti:

public interface Something {
    void doSomething();
}

@Singleton
public class SomethingImpl {
    @Inject
    AnotherThing anotherThing;

    @Inject
    public SomethingImpl() {
    }
}

Anda harus "mengikat" penerapan khusus ke antarmuka dengan file @Module.

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

Singkatnya untuk ini karena Dagger 2.4 adalah sebagai berikut:

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

4.) buat Injectorkelas untuk menangani komponen tingkat aplikasi Anda (menggantikan monolitik ObjectGraph)

(catatan: Rebuild Projectuntuk membuat DaggerApplicationComponentkelas pembangun menggunakan APT)

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.) buat CustomApplicationkelas Anda

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

6.) tambahkan CustomApplicationke AndroidManifest.xml.

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

7.) Masukkan kelas AndaMainActivity

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.) Selamat menikmati!

+1.) Anda dapat menentukan Scopeuntuk komponen Anda yang dengannya Anda dapat membuat komponen cakupan tingkat Aktivitas . Langganan memungkinkan Anda menyediakan dependensi yang hanya Anda perlukan untuk subskop tertentu, bukan di seluruh aplikasi. Biasanya, setiap Aktivitas mendapatkan modulnya sendiri dengan penyiapan ini. Harap diperhatikan bahwa penyedia terbatas ada per komponen , artinya untuk mempertahankan instance untuk aktivitas itu, komponen itu sendiri harus bertahan dari perubahan konfigurasi. Misalnya, itu bisa bertahan melalui onRetainCustomNonConfigurationInstance(), atau lingkup Mortar.

Untuk info lebih lanjut tentang berlangganan, lihat panduan oleh Google . Juga silakan lihat situs ini tentang metode penyediaan dan juga bagian ketergantungan komponen ) dan di sini .

Untuk membuat cakupan khusus, Anda harus menentukan anotasi pengualifikasi cakupan:

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

Untuk membuat subskop, Anda perlu menentukan cakupan pada komponen Anda, dan menentukan ApplicationComponentsebagai ketergantungannya. Jelas Anda juga perlu menentukan subskop pada metode penyedia modul.

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

    void inject(YourScopedClass scopedClass);
}

Dan

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

Harap diperhatikan bahwa hanya satu komponen cakupan yang dapat ditentukan sebagai dependensi. Anggap saja persis seperti bagaimana multiple inheritance tidak didukung di Java.

+2.) Tentang @Subcomponent: pada dasarnya, cakupan @Subcomponentdapat menggantikan ketergantungan komponen; tetapi alih-alih menggunakan builder yang disediakan oleh pemroses anotasi, Anda harus menggunakan metode pabrik komponen.

Jadi ini:

@Singleton
@Component
public interface ApplicationComponent {
}

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

    void inject(YourScopedClass scopedClass);
}

Menjadi ini:

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

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

Dan ini:

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

Menjadi ini:

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

+3.): Silakan periksa pertanyaan Stack Overflow lainnya tentang Dagger2 juga, mereka memberikan banyak info. Misalnya, struktur Dagger2 saya saat ini ditentukan dalam jawaban ini .

Terima kasih

Terima kasih untuk panduan di Github , TutsPlus , Joe Steele , Froger MCS dan Google .

Juga untuk panduan migrasi langkah demi langkah ini yang saya temukan setelah menulis posting ini.

Dan untuk penjelasan ruang lingkup oleh Kirill.

Lebih banyak informasi di dokumentasi resmi .

EpicPandaForce
sumber
Saya yakin kami melewatkan implementasi DaggerApplicationComponent
Thanasis Kapelonis
1
@ThanasisKapelonis DaggerApplicationComponentdibuat secara otomatis oleh APT pada build, tapi saya akan menambahkannya.
EpicPandaForce
1
Saya baru saja mempublikasikan metode Injector.initializeApplicationComponent karena CustomApplication saya berada di luar cakupan paket dan semuanya bekerja dengan sempurna! Terima kasih!
Juan Saravia
2
Agak terlambat tapi mungkin contoh berikut akan membantu siapa saja: github.com/dawidgdanski/android-compass-api github.com/dawidgdanski/Bakery
dawid gdanski
1
Jika Anda mendapatkan 'Peringatan: Menggunakan plugin yang tidak kompatibel untuk pemrosesan anotasi: android-apt. Ini dapat mengakibatkan perilaku yang tidak terduga. ' Pada langkah 1, ubah apt 'com.google.dagger: dagger-compiler: 2.7' menjadi annotationProcessor 'com.google.dagger: dagger-compiler: 2.7' dan hapus semua apt config. Rincian dapat ditemukan di sini bitbucket.org/hvisser/android-apt/wiki/Migration
thanhbinh84
11

Panduan untuk Dagger 1.x :

Langkah-langkahnya adalah sebagai berikut:

1.) tambahkan Daggerke build.gradlefile untuk dependensi

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

Juga, tambahkan packaging-optionuntuk mencegah kesalahan tentang duplicate APKs.

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

2.) membuat Injectorkelas untuk menangani 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.) Buat RootModuleuntuk menghubungkan modul masa depan Anda bersama-sama. Harap dicatat bahwa Anda harus memasukkan injectsuntuk menentukan setiap kelas di mana Anda akan menggunakan @Injectanotasi, karena jika tidak, Dagger melempar RuntimeException.

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

4.) Jika Anda memiliki sub-modul lain dalam modul Anda yang ditentukan di Root, buat modul untuk itu:

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

5.) membuat modul daun yang menerima dependensi sebagai parameter konstruktor. Dalam kasus saya, tidak ada ketergantungan melingkar, jadi saya tidak tahu apakah Dagger dapat menyelesaikannya, tetapi saya merasa itu tidak mungkin. Parameter konstruktor juga harus disediakan di Module by Dagger, jika Anda menentukannya complete = falsemaka bisa juga di Module lain.

@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.) Perluas Applicationdan inisialisasi file Injector.

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

7.) Di Anda MainActivity, panggil Injector dalam onCreate()metode tersebut.

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

8.) Gunakan @Injectdi MainActivity.

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

...

Jika Anda mendapatkan error no injectable constructor found, pastikan Anda tidak melupakan @Providesanotasi tersebut.

EpicPandaForce
sumber
Jawaban ini sebagian didasarkan pada kode yang dihasilkan oleh Android Bootstrap. Jadi, penghargaan untuk mereka karena telah memahaminya. Penggunaan solusi Dagger v1.2.2.
EpicPandaForce
3
Cakupan dagger-compilerharus providedjika tidak maka akan dimasukkan ke dalam aplikasi, dan itu di bawah lisensi GPL.
Denis Kniazhev
@deniskniazhev oh, saya tidak tahu itu! Terimakasih atas peringatannya!
EpicPandaForce