Skip to content Skip to sidebar Skip to footer

Trying To Get My Head Around Dependency Injection On Android With Dagger2

I have the following code working: SomeClass public class SomeClass { @Inject @Named('special') OkHttpClient mOkHttpClient; public SomeClass(Activity activity) {

Solution 1:

First let's have a look at what you have so far. The ActivityComponent is a little bit strange. @Singleton represents app-scoped singleton. Since ActivityComponent injects members that have the scope of an Activity and not the entire app, we probably need a new scope for that Component like this:

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

Now we can change that component:

@PerActivity@Component(dependencies = { AppComponent.class 
})
public interface ActivityComponent {

    voidinject(MainActivity mainActivity);

}

Notice we have now made it a dependent component of AppComponent. We will have to refactor AppComponent slightly to publish it's bindings to the dependent components.

Rationale: we want the ActivityComponent to be able to use the OkHttpClient that is bound in NetworkModule. However, NetworkModule is not a module of ActivityComponent - it is part of the parent AppComponent. Dependent components do not "automatically" inherit all of the bindings from their parents. In order for ActivityComponent to use the OkHttpClient as a dependency for the "special" OkHttpClient it needs to be exposed by the parent component. You can expose a binding to a dependent component by creating a method in the interface with the type you wish to expose. It's not necessary to expose all the bindings, just the very ones that you will use in the dependent components.

@Singleton@Component(modules = {
    ApplicationModule.class,
    NetworkModule.class
})
public interface ApplicationComponent {

    //injection sitesvoidinject(SomeClass someClass);

    //bindings published to subcomponentsOkHttpClientokHttpClient(); //default

}

Now extract a module for the "special" OkHttpClient:

@Module
public class SpecialNetworkModule {

    @Provides@PerActivity@Named("special")
    static OkHttpClient provideSpecialOkHttpClient(@Named("default") OkHttpClient okHttpClient) {
        returnokHttpClient.newBuilder()
        // Add .certificatePinner() here....build();
    }
}

and compose the ActivityComponent with that same module:

@PerActivity@Component(modules = { SpecialNetworkModule.class }
           dependencies = { AppComponent.class })
public interface ActivityComponent {

    voidinject(MainActivity mainActivity);

}

SomeClass is basically in the scope of your activity, so you can refactor it to get injected inside your activity by doing this:

publicclassSomeClass{

    privatefinal Activity activity;
    privatefinal OkHttpClient mOkHttpClient;

    @Injectpublic SomeClass(Activity activity, @Named("special") OKHttpClient client) {
        this.activity = activity;
        this.mOkHttpClient = client;
    }
}

Now make SomeClass a field of MainActivity (I am assuming you are using it there because it has a dependency on Activity and it is the only Activity code you have provided):

@Inject SomeClass someClass

@Override
public void onCreate() {

And make sure your ActivityComponent provides Activity. To do this you will need a new Module:

@Module@PerActivitypublicclassActivityModule{

    privatefinal Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    @Provides@PerActivity
    Activity activity() {
        returnthis.activity;
    }

    @Provides@PerActivity
    Context context() {
         returnthis.activity;
    }
}

And compose your ActivityComponent with this module:

modules = { SpecialNetworkModule.class, ActivityModule.class }

Now the consumption of the components needs a little bit of work. Get rid of the public ActivityComponent getActivityComponent() inside your Application. ActivityComponents should be generated inside, well, an Activity in order for them to track the scopes and lifecycles correctly.

So consuming the component inside your Activity should look something like this:

@Inject SomeClass someClass;

@OverridepublicvoidonCreate() {
     AppComponentappComponent= ((((MyApplication) getApplication()).getActivityComponent());
     DaggerActivityComponent.builder()
         .appComponent(appComponent)
         .activityModule(newActivityModule(this))
         .build()
         .inject(this);
}

Finally, to answer your two questions explicitly:

In a nutshell, how can I have some singleton dependencies tied to the application lifecycle while having other dependencies tied to some other lifecycle (be it an Activity or SomeClass like in my example) when they are dependent of singleton dependencies?

By creating a custom scope (@PerActivity), a component that tracks that scope (ActivityComponent), and using separate modules (SpecialNetworkModule, ActivityModule) for the narrower scoped dependencies. In doing this, you will need some form of relationship between the wider-scoped and narrower-scoped components. Which leads well to your next question:

How can I make the ActivityComponent dependent on the ApplicationComponent so that I only have to instantiate ApplicationModule once in the ApplicationComponent? Subcomponents? Component dependencies? I couldn't make this work with any approach...

As in the above example, using dependent components (subcomponents are also a possibility to consider). In doing this, make sure that wider-scoped components explicitly publish their bindings to their dependent components.

Post a Comment for "Trying To Get My Head Around Dependency Injection On Android With Dagger2"