Làm cách nào để thiết lập tính năng tiêm phụ thuộc DAGGER từ đầu trong dự án Android?


100

Làm thế nào để sử dụng Dagger? Làm cách nào để định cấu hình Dagger hoạt động trong dự án Android của tôi?

Tôi muốn sử dụng Dagger trong dự án Android của mình, nhưng tôi thấy nó khó hiểu.

CHỈNH SỬA: Dagger2 cũng đã ra mắt từ năm 2015 04 15, và nó thậm chí còn khó hiểu hơn!

[Câu hỏi này là "sơ khai" mà tôi đang thêm vào câu trả lời của mình khi tôi tìm hiểu thêm về Dagger1 và tìm hiểu thêm về Dagger2. Câu hỏi này mang tính chất hướng dẫn hơn là "câu hỏi".]



Cảm ơn vì đã chia sẻ điều này. Bạn có kiến ​​thức về cách chèn các lớp ViewModel không? Lớp ViewModel của tôi không có bất kỳ @AssistedInject nào nhưng nó có các phụ thuộc có thể được cung cấp bởi đồ thị Dagger?
AndroidDev


Một câu hỏi nữa, Với Dagger2, Liệu có thể có một đối tượng và tham chiếu của nó được chia sẻ bởi ViewModelPageKeyedDataSourcekhông? Giống như tôi sử dụng RxJava2 và muốn CompositeDisposable được chia sẻ bởi cả hai lớp và nếu người dùng nhấn nút quay lại, tôi muốn xóa đối tượng Disposable. Tôi đã thêm trường hợp ở đây: stackoverflow.com/questions/62595956/…
AndroidDev

Tốt hơn hết bạn nên đặt compositeDisposable vào bên trong ViewModelvà có thể chuyển đối số compositeDisposable giống như hàm khởi tạo của PageKeyedDataSource tùy chỉnh của bạn, nhưng tôi sẽ không thực sự sử dụng Dagger cho phần đó vì khi đó bạn cần các thành phần con được bổ sung và Hilt sẽ không thực sự hỗ trợ điều đó dễ dàng cho bạn.
EpicPandaForce

Câu trả lời:


193

Hướng dẫn cho Dagger 2.x (Phiên bản sửa đổi 6) :

Các bước như sau:

1.) thêm Daggervào build.gradletệp của bạn :

  • cấp cao nhất 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()
    }
}
  • cấp ứng dụng 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.) Tạo AppContextModulelớp của bạn cung cấp các phụ thuộc.

@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.) tạo AppContextComponentlớp cung cấp giao diện để lấy các lớp có thể đưa vào.

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

3.1.) Đây là cách bạn sẽ tạo một mô-đun với một triển khai:

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

Lưu ý:: Bạn cần cung cấp @Scopechú thích (như @Singletonhoặc @ActivityScope) trên @Providesphương thức chú thích của mô-đun để có được nhà cung cấp có phạm vi trong thành phần được tạo của bạn, nếu không nó sẽ không được mở và bạn sẽ nhận được một phiên bản mới mỗi khi bạn đưa vào.

3.2.) Tạo một thành phần có phạm vi ứng dụng chỉ định những gì bạn có thể đưa vào (điều này giống như injects={MainActivity.class}trong 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.) Đối với các phụ thuộc mà bạn có thể tự tạo thông qua một phương thức khởi tạo và không muốn xác định lại bằng cách sử dụng một @Module(ví dụ: bạn sử dụng các phiên bản xây dựng để thay đổi kiểu triển khai), bạn có thể sử dụng phương thức tạo @Injectchú thích.

public class Something {
    OtherThing otherThing;

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

Ngoài ra, nếu bạn sử dụng hàm @Injecttạo, bạn có thể sử dụng chèn trường mà không cần phải gọi rõ ràng component.inject(this):

public class Something {
    @Inject
    OtherThing otherThing;

    @Inject
    public Something() {
    }
}

Các @Injectlớp phương thức khởi tạo này được tự động thêm vào thành phần của cùng một phạm vi mà không cần phải chỉ định rõ ràng chúng trong một mô-đun.

Một lớp phương thức khởi tạo có @Singletonphạm vi @Injectsẽ được nhìn thấy trong @Singletoncác thành phần có phạm vi.

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

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

3.4.) Sau khi bạn đã xác định một triển khai cụ thể cho một giao diện nhất định, như sau:

public interface Something {
    void doSomething();
}

@Singleton
public class SomethingImpl {
    @Inject
    AnotherThing anotherThing;

    @Inject
    public SomethingImpl() {
    }
}

Bạn sẽ cần "ràng buộc" việc triển khai cụ thể với giao diện bằng một @Module.

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

Một cách ngắn gọn cho việc này kể từ Dagger 2.4 là như sau:

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

4.) tạo một Injectorlớp để xử lý thành phần cấp ứng dụng của bạn (nó thay thế nguyên khối ObjectGraph)

(lưu ý: Rebuild Projectđể tạo DaggerApplicationComponentlớp trình tạo bằng 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.) tạo CustomApplicationlớp học của bạn

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

6.) thêm CustomApplicationvào của bạn AndroidManifest.xml.

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

7.) Tiêm các lớp của bạn vàoMainActivity

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.) Tận hưởng!

+1.) Bạn có thể chỉ định Scopecho các thành phần của mình mà bạn có thể tạo các thành phần có phạm vi cấp Hoạt động . Kính con cho phép bạn cung cấp các phần phụ thuộc mà bạn chỉ cần cho một kính con nhất định, thay vì trong toàn bộ ứng dụng. Thông thường, mỗi Hoạt động có mô-đun riêng với thiết lập này. Xin lưu ý rằng một nhà cung cấp có phạm vi tồn tại trên mỗi thành phần , có nghĩa là để giữ lại phiên bản cho hoạt động đó, bản thân thành phần đó phải tồn tại khi thay đổi cấu hình. Ví dụ, nó có thể tồn tại qua onRetainCustomNonConfigurationInstance()phạm vi Mortar.

Để biết thêm thông tin về đăng ký, hãy xem hướng dẫn của Google . Ngoài ra, vui lòng xem trang web này về các phương pháp cung cấp và cả phần phụ thuộc thành phần ) và tại đây .

Để tạo phạm vi tùy chỉnh, bạn phải chỉ định chú thích bộ định mức phạm vi:

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

Để tạo một kính con, bạn cần chỉ định phạm vi trên thành phần của mình và chỉ định ApplicationComponentlà phần phụ thuộc của nó. Rõ ràng là bạn cũng cần chỉ định kính con trên các phương thức của nhà cung cấp mô-đun.

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

Xin lưu ý rằng chỉ một thành phần trong phạm vi có thể được chỉ định làm thành phần phụ thuộc. Hãy nghĩ về nó chính xác như cách đa kế thừa không được hỗ trợ trong Java.

+2.) Về @Subcomponentcơ bản, một phạm vi @Subcomponentcó thể thay thế một thành phần phụ thuộc; nhưng thay vì sử dụng trình tạo do bộ xử lý chú thích cung cấp, bạn cần phải sử dụng phương pháp nhà máy thành phần.

Vì vậy, điều này:

@Singleton
@Component
public interface ApplicationComponent {
}

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

    void inject(YourScopedClass scopedClass);
}

Trở thành cái này:

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

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

Và điều này:

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

Trở thành cái này:

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

+3.): Vui lòng kiểm tra các câu hỏi Stack Overflow khác liên quan đến Dagger2, chúng cung cấp rất nhiều thông tin. Ví dụ: cấu trúc Dagger2 hiện tại của tôi được chỉ định trong câu trả lời này .

Cảm ơn

Cảm ơn bạn về những hướng dẫn tại Github , TutsPlus , Joe Steele , Froger MCSGoogle .

Ngoài ra đối với hướng dẫn di chuyển từng bước này, tôi đã tìm thấy sau khi viết bài đăng này.

Và để giải thích phạm vi của Kirill.

Thậm chí nhiều thông tin trong tài liệu chính thức .


Tôi tin rằng chúng ta đang thiếu việc triển khai DaggerApplicationComponent
Thanasis Kapelonis

1
@ThanasisKapelonis DaggerApplicationComponentđược APT tự động tạo khi xây dựng, nhưng tôi sẽ thêm nó.
EpicPandaForce

1
Tôi chỉ cần đặt công khai phương thức Injector.initializeApplicationComponent vì CustomApplication của tôi nằm ngoài phạm vi gói và mọi thứ hoạt động hoàn hảo! Cảm ơn!
Juan Saravia,

2
Hơi muộn nhưng có thể những ví dụ sau sẽ giúp ích cho bất kỳ ai: github.com/dawidgdanski/android-compass-api github.com/dawidgdanski/Bakery
dawid gdanski

1
Nếu bạn nhận được 'Cảnh báo: Sử dụng các plugin không tương thích để xử lý chú thích: android-apt. Điều này có thể dẫn đến một hành vi không mong muốn. ' Trong bước 1, thay đổi apt 'com.google.dagger: dagger-compiler: 2.7' thành annotationProcessor 'com.google.dagger: dagger-compiler: 2.7' và xóa tất cả cấu hình apt. Chi tiết có thể tìm thấy tại đây bitbucket.org/hvisser/android-apt/wiki/Migration
thanhbinh84

11

Hướng dẫn cho Dagger 1.x :

Các bước như sau:

1.) thêm Daggervào build.gradletệp cho các phụ thuộc

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

Ngoài ra, hãy thêm packaging-optionđể ngăn lỗi về duplicate APKs.

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

2.) tạo một Injectorlớp để xử lý 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.) Tạo một RootModule để liên kết các mô-đun tương lai của bạn với nhau. Xin lưu ý rằng bạn phải bao gồm injectsđể chỉ định mọi lớp mà bạn sẽ sử dụng @Injectchú thích, vì nếu không thì Dagger sẽ ném RuntimeException.

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

4.) Trong trường hợp bạn có các mô-đun con khác trong các mô-đun được chỉ định trong Gốc của bạn, hãy tạo các mô-đun cho những mô-đun đó:

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

5.) tạo các mô-đun lá nhận các phụ thuộc làm tham số khởi tạo. Trong trường hợp của tôi, không có phụ thuộc vòng tròn, vì vậy tôi không biết liệu Dagger có thể giải quyết điều đó hay không, nhưng tôi thấy nó không chắc. Các tham số của hàm tạo cũng phải được cung cấp trong Mô-đun bởi Dagger, nếu bạn chỉ định complete = falsethì nó cũng có thể có trong các Mô-đun khác.

@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.) Mở rộng Applicationvà khởi tạo Injector.

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

7.) Trong của bạn MainActivity, hãy gọi Injector trong onCreate()phương thức.

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

8.) Sử dụng @Inject trong của bạn MainActivity.

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

...

Nếu bạn gặp lỗi no injectable constructor found, hãy đảm bảo rằng bạn không quên các @Provideschú thích.


Câu trả lời này một phần dựa trên mã được tạo bởi Android Bootstrap. Vì vậy, ghi công cho họ để tìm ra nó. Giải pháp sử dụng Dagger v1.2.2.
EpicPandaForce

3
Phạm vi dagger-compilernên providednếu không nó sẽ được đưa vào ứng dụng, và nó theo giấy phép GPL.
Denis Kniazhev

@deniskniazhev ồ, tôi không biết điều đó! Cảm ơn cho những người đứng đầu lên!
EpicPandaForce
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.