Document type: translation

The original link: https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3

Translator’s Note: This article is for academic exchange only, not for any commercial use, if there is infringement, please contact deletion.

Dagger version: 2.16

Dagger 2.10 has introduced a new dagger-Android module specifically designed for Android, besides the Dagger main module and the dagger-Compiler. In this article, we will introduce the method steps of using the Dagger Android. Of course, The premise is that you have knowledge of the Dagger.

This article focuses on Activity injection, but can also be used as a reference for injection of other Android components

Dagger common configuration

A common configuration for the Dagger on the Android side includes the Application Component, which is used to inject Activities, fragments, and other Android components, as well as the Application Module.

Application Component code

@Component(modules = { AppModule.class })
interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance Builder application(App application);
        AppComponent build(a);
    }    
    void inject(FeatureActivity featureActivity);
}
Copy the code

Application Module code

@Module
abstract class AppModule {
    @Provides 
    static Context provideContext(App application) {
        return application.getApplicationContext();
    }
    @Singleton 
    @Provides 
    static SomeClientApi provideSomeClientApi(a) {
        return newSomeClientApiImpl(); }}Copy the code

Application code for App

@Module
abstract class AppModule {
    @Provides 
    static Context provideContext(App application) {
        return application.getApplicationContext();
    }
    @Singleton 
    @Provides 
    static SomeClientApi provideSomeClientApi(a) {
        return newSomeClientApiImpl(); }}Copy the code

Since these components are already instantiated by the Android framework, we must perform member injection, annotating the visible class with the @Inject annotation as follows:

public class FeatureActivity extends AppCompatActivity {
  @Inject SomeClientApi mSomeClientApi; 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ((App)getApplication())
        .getAppComponent()
        .inject(this); }}Copy the code

However, this approach breaks the core maxim of dependency injection: a class should not know how it was injected, and the newly introduced Dagger android module addresses this problem by separating the injected class from the injector.

Use the new dagger-Android module

First, add the following Gradle dependencies to your build. Gradle file:

// Dagger core dependencies
annotationProcessor 'com. Google. Dagger: a dagger - compiler: 2.16'
implementation 'com. Google. Dagger: a dagger: 2.16'
// Dagger Android dependencies
annotationProcessor 'com. Google. Dagger: a dagger - android - processor: 2.16'
implementation 'com. Google. Dagger: a dagger - android: 2.16'
implementation 'com. Google. Dagger: a dagger - android - support: 2.16'
Copy the code

In our project, the configuration of the new module requires more than one Component and module. Each Activity needs its own subComponent and connects it to the Application Component. Here’s my suggested class structure:

/
| App (extending Application)
| AppComponent
| AppModule
| ContributeAndroidInjectorsModule / / 3
+ feature/
 | FeatureModule / / 2
 | FeatureActivityModule / / 1
 | FeatureActivity
Copy the code

As we can see, three additional classes have been added to the typical configuration mentioned above. Each feature has its own component and module.

Note: I use the term “feature” to describe the display interface (or Activity) in an app.

1.FeatureActivityModule

As mentioned earlier, each Activity now needs its own subComponent, so Dagger introduces a handy annotation to remind us of the code that generates the subComponent. Something like this:

@Module
public abstract class FeatureActivityModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = { FeatureModule.class })
    abstract FeatureActivity contributeFeatureActivityInjector(a);
}
Copy the code

2.FeatureModule

We configure the binding for the Activity itself in this Module. Bindings in this Module are only valid for the Activity and its subComponent, unless the Module is used in other Components. Assuming we’re using MVP design patterns, here’s how you would normally bind a View:

@Module
abstract class FeatureModule {

    @ActivityScope
    @Binds
    abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}
Copy the code

3.ContributeActivityModule

So far, we have told Dagger to generate a subComponent for our Activity, but we have not connected the subComponent to Applicaiton’s Component. To do this, we need another module to hold all the Activity modules. This Module will be included in the Application Component.

@Module(includes = {
        FeatureActivityModule.class
        // Other Activity modules will be added here
})
abstract class ContributeActivityModule {}Copy the code

4.AppComponent

Component configuration for Applicaiton (using Kotlin)

I prefer to write the Dagger module in Java, because Kotlin lacks the static modifier, so we need to add an extra @Module annotation to the same class when we use the Companion Object, which I don’t like.

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class, // 1
    ContributeActivityModule::class, // 2
    AppModule::class
])
interface AppComponent : AndroidInjector<App> { // 3
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<App>() // 4
}
Copy the code

(The Java code is as follows)

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class, // 1
    ContributeActivityModule::class, // 2
    AppModule::class
])
public interface AppComponent extends AndroidInjector<App> { // 3

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

Note some changes in the AppComponent:

  • In order to make the Dagger Android can run normally, we in the Application of the Component includes AndroidSupportInjectionModule
  • ContributeActivityModule is included to connect the Activity’s subComponent to the AppComponent
  • The AppComponent must inherit from AndroidInjector and generically set it toApplicationclass
  • The Appcomponent Builder can optionally inherit androidInjector. Builder and provide Application classes. Because the Base class already implements the common Component Builder, which contains a method to set up an Application instance and another method to build a Component, it’s easy to introduce some lines of code.

5.And then we modify oursAppFile to make it inheritDaggerApplicationAnd implement the basic method, the code is as follows

class App : DaggerApplication() {
    private val appComponent: AndroidInjector<App> by lazy {
        DaggerAppComponent
                .builder()
                .create(this)}override fun applicationInjector(a): AndroidInjector<out DaggerApplication> {
        return appComponent
    }
}
Copy the code

(The Java code is as follows)

public class App extends DaggerApplication {

    private AppComponent appCompoent;

    @Override
    public void onCreate(a) {
        appCompoent = DaggerAppComponent.builder()
                .application(this)
                .build();

        super.onCreate();
    }

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        returnappCompoent; }}Copy the code

6.Finally, in theFeatureActivityIn, we remove the code we wrote earlier to inject into the Activity and make it inherit fromDaggerAppCompatActivityRather thanAppCompatActivity

class FeatureActivity : DaggerAppCompatActivity(), FeatureView {
    @Inject
    internal lateinit var presenter: FeaturePresenter

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.feature_activity)
    }
}
Copy the code

(The Java code is as follows)

public class FeatureActivity extends DaggerAppCompatActivity implements FeatureView {
    public static final String EXTRA_SOME_ID = "some_id";
    @Inject FeaturePresenter presenter;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); setContentView(R.layout.feature_activity); }}Copy the code

Inject custom parameters into the Acivity Component

In some cases you want to inject some parameters provided by the Activity itself. The traditional method is to pass parameters to the Module constructor before calling the Inject () method of the Activity’s component. For example, if we follow the MVP design pattern, our Presenter will take View as part of its constructor argument, which means we need to pass the Activity as an argument to the Module’s constructor. Before using the dagger, we can write it like this:

@Module
class FeatureModule {
    private FeatureView view;

    public FeatureModule(FeatureView view) {
        this.view = view;
    }

    @Provides FeatureView provideView(a) {
        returnview; }}Copy the code

Also, in Presenter we use constructor injection:

class FeaturePresenter {
    private final FeatureView view;
    
    @Inject
    Presenter(FeatureView view) {
        this.view = view;    
    }
    public void doSomething(a) {}}Copy the code

Finally, build Component, pass in a new instance of mudle, and inject the Activity:

class FeaturePresenter {
    private final FeatureView view;
    
    @Inject
    Presenter(FeatureView view) {
        this.view = view;    
    }
    public void doSomething(a) {}}Copy the code

But how do we do this with the new dagger-Android module? After all, we no longer need to inject activities manually, so we don’t have the same access to module creation as before.

The answer is, we don’t need to do that anymore. With the dagger- Android module, the Activity is already part of the diagram. What exactly does that mean? As you’ll recall, we configured the FeatureModule to bind to FeatureActivity wherever FeatureView is used:

@Module
public abstract class FeatureModule {
    @Binds
    abstract FeatureView provideFeatureView(FeatureActivity featureActivity); }}Copy the code

However, what if we want to pass parameters to Presenter through an Intent that accepts an Activity? Suppose you pass a unique ID in the Activity’s Intent extra, and a Presenter needs this ID. For example, a Presenter needs this ID to make an HTTP request. The way to do this is to use the @named annotation as the qualifier. So our Presenter code looks like this:

class FeaturePresenter {
    private FeatureView featureView;
    private String someId;

    @Inject
    public FeaturePresenter(FeatureView featureView, @Named("someId") String someId) {
        this.featureView = featureView;
        this.someId = someId;
    }

    public void foo(a) { featureView.showFoo(); }}Copy the code

The answer is simple. We added a Provides method to the FretureModule, which will hold an instance of FeatureActivity and get the information we need from the FeatureActivity extras:

@Module
abstract class FeatureModule {

    @ActivityScope
    @Binds
    abstract FeatureView provideFeatureView(FeatureActivity featureActivity);

    @ActivityScope
    @Provides
    @Named("someId")
    static String provideSomeId(FeatureActivity featureActivity) {
        returnfeatureActivity.getIntent().getStringExtra(FeatureActivity.EXTRA_SOME_ID); }}Copy the code

As mentioned earlier, this implementation is possible because FeatureActivity is already in the icon.

As mentioned at the beginning of this article, Fragment injection and other Android components are outside the scope of this article. Injected Fragment objects are described in the official documentation, and I strongly encourage you to read it. There is more information about Services, Receivers, and ContentProviders.

conclusion

The Dagger- Android module is very close to Dependency injection for Android. If you are developing a new project and are going to use Dagger, you must use the dagger-Android configuration. The official example is too simple for me, so you can check out my own Demo.