How does an Android application in one gradle module include dagger #Modules defined in an other Android Library (.aar)?
I'm trying to have a generic library that provides a set of functionality defined thru dagger #Module's able to have have them injected into Applications that depend on that library
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
LibraryAppModule.class
})
public interface LibraryComponent extends AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
LibraryComponent build();
}
void inject(LibraryApplication app);
#Override
void inject(DaggerApplication instance);
}
#Module
public class LibraryAppModule {
#Provides
#Singleton
Context provideContext(Application application) {
return application;
}
#Singleton
#Provides
ServiceFoo provideServiceFoo(Context context) {
return new ServiceFoo(context);
}
#Singleton
#Provides
ServiceBar provideServiceBar(Context context, ServiceFoo serviceFoo) {
return new ServiceBar(context, serviceFoo);
}
}
Now in the Application that has a dependency for this library (build.gradle) I've tried the following:
implementation project(":Library")
Application using the library
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
LibraryAppModule.class
})
public interface ExampleApplicationComponent extends AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
ExampleApplicationComponent build();
}
void inject(ExampleApplication app);
#Override
void inject(DaggerApplication instance);
}
This is an example of the place where I'm getting the issue (a null object). This is in a separate module that depends on the library.
public class MainActivity extends AppCompatActivity {
#Inject
ServiceFoo serviceFoo;
#Inject
ServiceBar serviceBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceFoo.downloadImages();
serviceBar.uploadImages();
}
}
I expect to see the LibraryAppModule's services to be available in my Activities using #Inject. What am I doing wrong?
In order to resolve the issue 2 things needed to be done.
The MainActivity had to extend DaggerAppCompatActivity (this ensures during onCreate() for our Activity that inject() is called, but for this to work there needs to be an AndroidInjector for the Activity.
This is accomplished by using #ContributesAndroidInjector within a module that I defined and installed into the ExampleApplicationComponent
#Module
public abstract class ExampleApplicationActivityBuilder {
#ContributesAndroidInjector
abstract MainActivity bindMainActivity();
}
Now within the MainActivity, the Services I want injected from the library module are non-null.
Here's is a git commit that shows the difference implementing this fix.
https://github.com/PeregrineFalcon/DaggerExampleLibrary/commit/1ebfdecfa3684c7ac124b9f6a4cecd23712a74fd
Hope this helps someone else who trying to do a multi-module Dagger Android project with #Modules provided through the library.
A class that wants a property injected into it should call inject()!
Since you want serviceFoo injected in MainActivity, you should call inject() from onCreate(). However, where is inject() defined for you to just call it? Hence, you additionally declare inject() in ExampleApplicationComponent.
All that said, it looks like your understanding of dagger is incomplete. Recommend this blog post.
Related
I'm confused about scoped dependencies in Dagger using dagger-android.
Using #ContributesAndroidInjetor I have a code something like the following:
#Module
public abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = PotatoesModule.class)
public abstract MainActivity contributeMainActivityInjector();
#ContributesAndroidInjector
public abstract UserActivity contributeUserActivity();
}
The ActivityBindingModule is defined as a module in my AppComponent. But the problem is. How can I do something like
#UserScope
#Component(dependencies = AppComponent.class)
public interface UserComponent {...}
And annotate an Activity to use that scope? Is all my dependencies inside activity "local singletons"? Because each Activity injector is a subcomponent of AppComponent.
Maybe I'm not understanding the concept of "scopes" using dagger-android, I would be glad if someone could explain it.
Here's some clarification on scopes:
Say you had an AppComponent and you Annotate it with the #Singleton annotation:
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class
})
public interface AppComponent extends AndroidInjector<BaseApplication> {
#Component.Builder
interface Builder{
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
And you had an AppModule which provide App level dependencies (i.e. a Retrofit Instance for example that you annotate with #Singleton):
#Module
public class AppModule {
#Singleton
#Provides
static Retrofit provideRetrofitInstance(){
return new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
Then we can say that AppComponent owns the #Singleton scope and therefore the #Singleton annotation that you put on the Retrofit instance that you provided now has the same scope as the AppComponent - i.e. it's an application level scope.
If you want to scope to Activities - you should make a custom scope like this:
#Scope
#Documented
#Retention(RUNTIME)
public #interface UserScope {
}
Then in your ActivityBindingModule (that you've written), annotate the UserActivity with #UserScope if you want the UserActivity to "own" the #UserScope scope. Also, add a module next to the #ContributesAndroidInjector - let's call it UserModule.class:
#Module
public abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = PotatoesModule.class)
public abstract MainActivity contributeMainActivityInjector();
#UserScope
#ContributesAndroidInjector(modules = UserModule.class)
public abstract UserActivity contributeUserActivity();
}
And now, creating UserModule.class and annotating a provided dependency with #UserScope:
#Module
public class UserModule {
#UserScope
#Provides
static User provideUser(){
return new User();
}
}
This dependency now has the same scope as the UserActivity. So when UserActivity is destroyed and re-created, the dependency provided will also be destroyed and recreated.
To finish up:
Create a POJO User:
public class User {
public User() {
}
}
and now, if you go to your UserActivity and do:
public class UserActivity extends DaggerAppCompatActivity {
private static final String TAG = "UserActivity";
#Inject
User aUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
Log.d(TAG, "onCreate: " + aUser);
}
}
If you run your App now you will see a memory address being printed to the log. Rotate the device to destroy and re-create the activity and you'll see that the memory address changes. This is how we know that the #UserScope is working correctly.
If you want to see your Application scope in action i.e. #Singleton, then create an AppModule, add it to your AppComponent and provide a User dependency in that module and annotate it with #Singleton. Remember to use the #Named annotation too since you now have 2 dependencies that are provided with the same return type (that can both be accessed within the Activity Scope).
Go to your UserActivity again and Inject both Users (remember to use #Named). Log it in another logging statement and after rotating the device you will notice you have the same memory address for the Application scoped dependency.
I hope this cleared things up.
Is all my dependencies inside activity "local singletons"? Because each Activity injector is a subcomponent of AppComponent.
The subcomponents generated by dagger-android are unscoped, unless you annotate the #ContributesAndroidInjector-annotated method with a scope.
But the problem is. How can I do something like ... #Component(dependencies = AppComponent.class) ... And annotate an Activity to use that scope?
As far as I know, you can only use subcomponents with dagger-android. Furthermore activity subcomponents must be declared in modules installed in the application component, while fragment subcomponents may be declared in modules installed in either application, activity or fragment components.
I'm not sure what you mean by "annotate an Activity to use that scope" though.
I'm using Dagger Android to link dependencies in my Android application.
I have 2 Activities, both have their own Scope (#ActivityScoped).
Here is my code so far :
MyApplication.java
public class MyApplication extends Application implements HasActivityInjector {
#Inject DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBindingModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application app);
AppComponent build();
}
void inject(MyApplication app);
}
ActivityBindingsModule.java
#Module(subcomponents = ActivityComponent.class)
public abstract class ActivityBindingModule {
#ActivityScoped
#ContributesAndroidInjector
public abstract MainActivity contributeMainActivityInjector();
#ActivityScoped
#ContributesAndroidInjector
public abstract SecondActivity contributeSecondActivityInjector();
}
Let's say I have a class MyClass tied to ActivityScoped that I want to inject into both Activities :
MyClass.java
#ActivityScoped
public class MyClass {
#Inject
public MyClass(Activity activity) {
// Do something with the activity instance...
}
}
What I want to achieve is to inject into MyClass the Activity instance of the enclosing scope.
For example, if MyClass is injected into ActivityA, then the activity constructor argument of MyClass should be an instance of ActivityA.
What I tried :
#Binds annotated method to inject ActivityA and ActivityB as Activity. This doesn't work since Dagger does not allow binding multiple implementations to the same base type.
Map Multibindings doesn't suit my needs : I don't want MyClass to know about the Activity in which it is injected.
Maybe I'm missing something, as dependency injection is rather hard to setup. How can I do that with Dagger Android ?
#Binds annotated method to inject ActivityA and ActivityB as Activity. This doesn't work since Dagger does not allow binding multiple implementations to the same base type.
That's the right way to do so. You only get an error when you try to bind multiple implementations to the same type in the same component. So you should not bind both within your AppComponent, but to the #ActivityScoped subcomponents.
You have to create a module for each Activity, A and B, that binds the implementation to Activity. Then you just add that module to the Subcomponent that Dagger creates for you.
#ActivityScoped
#ContributesAndroidInjector(modules={MainActivityBindingsModule.class})
public abstract MainActivity contributeMainActivityInjector();
That way the binding only exists within the #ActivityScoped component and you should be able to repeat it.
I'm having a similar problem like the one in this question.
While the accepted answer does help, but I'm missing final piece to solve the problem.
I have 2 android library modules: common and exp which depends on common.
Everything under common:
#Module
public class CommonModule {
#Singleton
#Provides
public Repository providesRepository() {
return new Repository();
}
}
#Singleton
#Component(modules={CommonModule.class})
public interface CommonComponent {
void inject(CommonClass commonClass);
/**
CommonClass needs instance of Repository
**/
}
public class CommonDIHolder {
public static CommonComponent sComponent;
public static void init() {
sComponent = DaggerCommonComponent.builder().build();
}
}
Everything under exp:
#Module(includes={CommonModule.class})
public class ExpModule {
#Singleton
#Provides
public ExpResource provideExpResource() {
return new ExpResource();
}
}
#Singleton
#Component(modules={ExpModule.class}, dependencies={CommonComponent.class})
public interface ExpComponent {
void inject(ExpClass expClass);
/**
ExpClass needs instance of Repository and ExpResource
**/
}
public class ExpDIHolder {
public static ExpComponent sComponent;
public static void init() {
sComponent = DaggerExpComponent.builder()
.commonComponent(CommonDIHolder.sComponent)
.build();
}
}
I need both CommonClass and ExpClass receive the same instance of Repository.
The problem with this approach is that #Singleton can't depends on #Singleton. So I have to change the scope of ExpComponent into self-defined scope called #ExpScope. Then I changed the provideExpResource into #ExpScope as well.
Then I encountered an error saying that ExpComponent may not reference bindings with different scopes. It refers to the provideRepository which has different scope (#Singleton) on it. If I changed the scope into ExpScope then the CommonComponent will have different scope with provideRepository.
If I changed all #Singleton into #ExpScope then I receive this error message: depends on scoped components in a non-hierarchical scope ordering
What should I do? Or I'm doing the wrong approach here?
Use one and only one #Singleton scoped component
You should have one and only one #Singleton scoped component like this:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
}
Only specify Activities, Fragments, and Services as explicit injection targets for Components
In an Android app, you should only list Activities, Fragments and Services as injection sites. You should configure Dagger 2 to inject the rest of your dependencies without having to resort to calling component.inject(this) inside them.
For example, if your CommonClass looks like this:
public class CommonClass {
#Inject Repository repository;
public class CommonClass() {
CommonComponentHolder.get().inject(this);
}
}
Refactor it like this:
public class CommonClass {
private final Repository repository;
#Inject
public class CommonClass(Repository repository) {
this.repository = repository;
}
}
Now when you have an Activity or Fragment that needs CommonClass and you are injecting with CommonComponent or one of its sub-components or dependent components, they can obtain instances of CommonClass wired with the correct dependencies:
public class MyActivity extends AppCompatActivity {
#Inject CommonClass commonClass;
public void onCreate(Bundle savedInstanceState) {
CommonComponentHolder.getComponent().inject(this);
}
}
Use subcomponents or dependent components to specify the injection targets
Now you have a #Singleton scoped component, you'll probably want to create a component for a narrower scope for your Activity or Fragment. You'll have to connect it to your CommonComponent, so use dependent components or subcomponents (subcomponents are preferred as of Dagger 2.10). Since you say you have already tried defining a #ExpScope, I think the missing piece is to make subcomponent or dependent component with the #ExpScope that injects your Activity or Fragment.
Something like the following for the top-level singleton component:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
ExpComponent.Builder exComponent();
}
And then for the subcomponent:
#ExpScope
#Subcomponent(modules = {NarrowerScopedModule.class})
public interface ExpComponent {
#Subcomponent.Builder
public interface Builder {
Builder narrowerScopedModule(NarrowerScopedModule narrowerScopedModule);
ExpComponent build();
}
}
There are good working examples of Android projects in the Google Android Architecture Blueprints Github repo
Hello I am new to Dagger2.
Goal. Take my Networking DI and MVP DI. MVP as in the presenter for an an activity that extends base activity. I want to combine all this into one super module and place this into my base activity. Over time add more presenters.
I do not want 30+ inject statements in my baseActivity.
I am following this example but it is too simple compared to what I am trying to do.
I think the issue is with injecting the API at the base activity. For some reason Dagger is looking for Api in my MVP class.. So that would be a dependency graph issue?
Having spent more time on this.. The issue stems from Mvp's interface of baseActivity or any sub activity that extends baseActivity. That means when it goes to inject, it sees the #inject Api call, and cannot find it. It will work if I add Api to this module, but thats upside down of what I want. I want Component / Module for Application level items. I then want a component / module that has all my different MVP component in one module.. It's like Dagger starts looking for dependencies in the leaf of a tree and getting upset when it doesn't see whats in the root. I need it to go the other way. Be satisfied that I injected the dependency in the Root activity.
Base Activity...
#inject
public ApiClient mClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mManager = new SharedPreferencesManager(this);
DaggerInjector.get().inject(this);
}
DaggerInjector
public class DaggerInjector {
private static AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
public static AppComponent get() {
return appComponent;
}
}
#Component(modules = {AppModule.class, ApiModule.class, MvpModule.class})
#Singleton
public interface AppComponent {
void inject(BaseActivity activity);
}
Api
#Singleton
#Component(modules = {ApiModule.class})
public interface ApiComponent {
void inject( BaseActivity activity);
}
#Module
public class ApiModule {
#Provides
#Singleton
public ApiClient getApiClient(){
return new ApiClient();
}
}
Mvp
#Singleton
#Component(modules = {MvpModule.class})
public interface MvpComponent {
void inject(BaseActivity activity);
}
#Module
public class MvpModule {
#Provides
#Singleton
public MvpPresenter getMvpPresenter(){ return new MvpPresenter();}
}
Error:(16, 10) error: ApiClient cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method. This type supports members injection but cannot be implicitly provided.
ApiClient is injected at
...BaseActivity.ApiClient
...BaseActivity is injected at
MvpComponent.inject(activity)
I found out my problem. I needed to use a subcomponent.
#Singleton
#Subcomponent(modules = {MvpModule.class})
public interface MvpComponent {
void inject(BaseActivity activity);
}
#Module
public class MvpModule {
#Provides
#Singleton
public MvpPresenter getMvpPresenter(){ return new MvpPresenter();}
}
see
Dagger- Should we create each component and module for each Activity/ Fragment
Dagger2 activity scope, how many modules/components do i need?
I am not quite sure how to solve this with dagger 2.
Lets assume we have ApplicationModule that provides us ApplicationContext
then we have ApplicationComponent that uses just this one module.
Then on top of it we have ActivityModule and ActivityComponent that has dependency on ApplicationComponent.
ActivityComponent is build just like
ApplicationComponent component = ((MyApplication) getApplication()).getComponent();
mComponent = Dagger_ActivityComponent.builder()
.applicationComponent(component)
.activityModule(new ActivityModule(this))
.build();
And then I inject my activity:
mComponent.inject(this);
Now I am able to use everything that is declared inside my ActivityModule, however it is not possible for me to access ApplicationModule.
So the question is how could that be achieved? So that when I build component that depends on another component I can still access module from the first one?
EDIT
I think I have found solutions, after rewatching Devoxx talk by Jake again, I have had to miss that out, whatever I want to use from another components module I have to provide in that component, for example I want to use Context from ApplicationModule then inside ApplicationComponent I have to state Context provideContext(); and it is going to be available. Pretty cool :)
You have already answered your question, but the answer is to specify the provision methods in your "superscoped" component (ApplicationComponent).
For example,
#Module
public class ApplicationModule {
#Provides
#Singleton
public Something something() {
return new Something.Builder().configure().build();
// if Something can be made with constructor,
// use #Singleton on the class and #Inject on the constructor
// and then the module is not needed
}
}
#Singleton
#Component(modules={ApplicationModule.class})
public interface ApplicationComponent {
Something something(); //PROVISION METHOD. YOU NEED THIS.
}
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
#ActivityScope
public class OtherThing {
private final Something something;
#Inject
public OtherThing(Something something) {
this.something = something;
}
}
#Component(dependencies = {ApplicationComponent.class})
#ActivityScope
public interface ActivityComponent extends ApplicationComponent { //inherit provision methods
OtherThing otherThing();
}