I have two components, one for Application Context and one for Activity Context as shown below.
#Singleton
#Component(modules = {AppModule.class,})
public interface AppComponent {
#ForApplication
Context context();
//Preferences prefs(); //(Question is related to this line)
}
#PerActivity
#Component(dependencies = AppComponent.class,modules = {ActivityModule.class})
public interface ActivityComponent extends AppComponent {
void inject(ActivityA activity);
}
I have a Singleton class that I want to inject in ActivityA that is declared in ActivityComponent.
#Singleton
public class Preferences {
#Inject
public Preferences(#ForApplication Context context) {
...
}
}
public class ActivityA extends AppCompatActivity {
#Inject
Preferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerActivityComponent.builder()
.appComponent(((MyApplication) getApplication()).getComponent())
.activityModule(new ActivityModule(this))
.build();
}
I am injecting AppComponent in my Applicaiton onCreate() and ActivityComponent in onCreate of Activity, in this case ActivityA like above
Problem(or Question): Currently, If I do not expose this singleton class from my AppComponent (The commented line in first code block) and do not have provides method in AppModule. I cannot compile. Compilation Error shows I cannot have a reference to different scope from ActivityComponent, which I kind of understand. That means, I cannot access Singleton scoped class with PerActivity scoped component.
But, do I have to have provides method for all Singleton class and expose it via AppComponent (which I am currently doing and works)? Is there a better way to do the Singleton class injection?
There is another way. Declare Activity component as Application subcomponent. This way everything declared in Application component is visible in Activity subcomponent.
Access to the Activity subcomponent can be done through Acpplication component:
GithubClientApplication.get(this)
.getAppComponent()
.provide(new ActivityModule(this))
Have a look at this excellent article about Dagger components, especially in Implementation section.
Related
I can't understand what wrong I am doing here
Splash Activity :
public class SplashActivity extends BaseActivity implements SplashView {
#Inject
SplashPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_with_di);
presenter.getAppVersion();
}
}
Component :
#Component(modules = SplashModule.class)
public interface AppComponent {
SplashWithDiPresenter getSplashWithDiPresenter();
}
Splash Module :
#Module(includes = RetrofitModule.class)
public class SplashModule {
#Provides
SplashPresenter provideSplashPresenter(final SplashInteractorImpl interactor){
return new SplashPresenterImpl(interactor);
}
#Provides
SplashInteractor providesSplashInteractor(final ApiInterface apiInterface){
return new SplashWithDiInteractorImpl(apiInterface);
}
}
inside application class called this method in onCreate()
private void createComponent() {
appComponent = DaggerAppComponent.builder()
.splashModule(new SplashModule())
.build();
}
Getting null object reference on Splash activity on create method
-> presenter.getAppVersion();
You have injected your application dependencies and now you need to do the same with SplashActivity dependencies. So you need to create a new component for your activity, lets say SplashComponent, and add inject method to it like this:
#PerActivity
#Component(modules = SplashModule.class, dependencies = AppComponent.class)
public interface SplashComponent {
public void inject(SplashActivity activity);
}
And then in your SplashActivity in the onCreate method add injection like this:
public class SplashActivity extends BaseActivity implements SplashView {
#Inject
SplashPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_with_di);
DaggerSplashComponent.builder()
.appComponent(getAppication().getAppComponent())
.splashModule(new SplashModule())
.build()
.inject(this);
presenter.getAppVersion();
}
}
Note, that you need to call your presenter's method only after you have injected your dependencies with inject method!
Ideally you should look at using subcomponents for these use cases for injecting Android Components(Activities, Fragments, Services...). It's pretty simple and avoids coupling Injector Components with Android components enabling testing of these classes and switching of components for Espresso tests.
Add dagger-android deps to your project (com.google.dagger:dagger-android-support:2.x, com.google.dagger:dagger-android-processor:2.x)
And then make an abstract DepsModule module, for example -
#Module
public abstract class DepsModule {
#PerActivity
#ContributesAndroidInjector(modules={SplashModule.class})
SplashActivity provideSplashInjector()
}
And change you App Component to include AndroidSupportInjectionModule.class & DepsModule.class and you should remove SplashModule.classs from the list to avoid rebinding errors.
Now its as easy as having a DispatchingAndroidInjector<Activity> instance injected to you App class and implementing HasActivityInjector interface to return the dispatching injector.
Now in SplashActivity before invoking super.onCreate() call AndroidSupportInjection.inject(this)
#ContribbutesAndroidInjector automatically tells dagger to create a sub-component for SplashActivity and bind the injector factory with DispatchingAndroidInjector<Activity> removing the need for boilerplate sub-component classes
And other sub-component installations for different activities can be added to DepsModule to enable injecting them in a similar way.
This also helps in scope segregation as exposing a splash activity's bindings to the entire app component is not really good
Hope I could help. You can visit the dagger 2 android docs for more info on using Dagger 2
According to tutorials like this one: in Dagger2 we are able to provide local singleton objects using a custom scope.
I already have a global appComponent and my goal is to create a activity scope that allows a subcomponent to have local singleton providers.
My issue is when i create a custom scope and inject my activity twice, i see that the object being provided is not the same one, even though i tagged it with the custom scope. for the appComponent which uses the #Singleton annotation it works fine though.
Lets take a look at how i did this:
Here is the custom activity scope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
And here is how i define the subcomponent:
#ActivityScope
#Subcomponent(modules = {PresenterModule.class, UseCaseModule.class})
public interface ActivitySubComponent {
void inject(LoginActivity target);
void inject(LoginPresenter target);
void inject(UCGetFireBaseAccount target);
}
Notice here that i've tagged this subcomponent with #ActivityScope annotation that i custom made above.
Now lets see the appComponent which is the parent component and global:
#Singleton
#Component(modules = {AppModule.class, NetworkModule.class, RepositoryModule.class})
public interface AppComponent {
void inject(myappCloudRepo target);
void inject (FireBaseCloudRepo target);
ActivitySubComponent plus(PresenterModule presenterModule, UseCaseModule useCaseModule);
}
Notice ActivitySubComponent is defined here as a subcomponent.
Finally lets look at the presenterModule class to see what i wanted to be provided as a singleton:
#Module
public class PresenterModule {
#Provides
#ActivityScope
LoginPresenter provideLoginPresenter(Context context) {
return new LoginPresenter(context);
}
}
Now when i actually use this i set it up in my application class like this:
public class MyApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = initDagger(this);
}
public AppComponent getAppComponent() {
return appComponent;
}
protected AppComponent initDagger(MyApplication application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
public ActivitySubComponent getActivityComponent() {
return appComponent.plus(new PresenterModule(),new UseCaseModule());
}
}
Then lastly in any activity i do this:
public class LoginActivity extends AppCompatActivity{
ActivitySubComponent activityComponent;
//this loginpresenter is from the activitysubcomponent
#Inject
LoginPresenter loginPresenter;
//this retrofit object is from the appcomponent
#Inject
Retrofit retrofit;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityComponent= ((MyApplication)getApplication()).getActivityComponent();
activityComponent.inject(this);
Log.d("j2emanue",loginPresenter.toString());
Log.d("j2emanue",retrofit.toString());
activityComponent= ((MyApplication)getApplication()).getActivityComponent();
activityComponent.inject(this);
Log.d("j2emanue2",loginPresenter.toString());
Log.d("j2emanue2",retrofit.toString());
}
}
When i take a look at the logs after injecting TWICE i am expecting all objects to be the same but instead the loginPresenter is another instance but the retrofit is the same so that works.
Here is the log:
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter#1c27a460
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: retrofit2.Retrofit#13366d19
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter#3dc041de
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: retrofit2.Retrofit#13366d19
Notice the loginpresenter is another object so its not making a local singleton for me that would only be destroyed when i dereference the activitysubcomponent.
what am i missing here ? The reason i want it to be a local singleton is for configuration changes. This way the presenter can be maintained during a config change.
In your application class you create and return a new subcomponent.
public ActivitySubComponent getActivityComponent() {
return appComponent.plus(new PresenterModule(),new UseCaseModule());
}
Now scopes mean that within a scope an object exists only one time. But in your case, you create 2 different components. Those 2 components will share everything in their parent component (they are subcomponents), but anything within their scope will be recreated, but unique to their scope.
Anything within your component will use the same #ActivityScope annotated objects, but if you create 2 components you will have 2 copies of everything.
If you take #Singleton as an example, it does not mean the object will be an actual Singleton. It is the name of the scope that you are supposed to have for your root component, which should only be created once and be kept during the lifetime of the application.
If you were to create 2 AppComponents that are #Singleton you could observe that same behavior—two different instances of your object.
In your example Retrofit is the same, because you use the same AppComponent both times, but LoginPresenter gets recreated along with every ActivitySubComponent that you create.
With Dagger you should try that your components follow the same lifecycle as what they scope, thus your app should hold an AppComponent, and every Activity should have their own ActivityComponent (keep the component as a member variable!). When you create a new Activity you should create a new #ActivityScope scoped component, but not more often than that.
You should remove your getActivityComponent() from the Application and keep a reference to your ActivitySubComponent, because injecting scoped dependencies with the same component will give you the same objects.
activityComponent.inject(this);
activityComponent.inject(this);
// call it as many times as you'd like.
activityComponent.inject(this);
Just don't recreate your component.
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?
According code below:
#Module
public class AppModule {
private Application application;
public AppModule(Application application) {
this.application = application;
}
#Singleton
#Provides
Context providesContext() {
return application;
}
#Singleton
#Provides
IAppDbHelper providesAppDbHelper() {
// a SQLiteOpenHelper class
return new AppDbHelper(application);
}
}
AppComponent:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(MainActivity mainActivity);
void inject(SecondActivity secondActivity);
IAppDBHelper providesIAppDBHelper();
}
MainActivity:
public class MainActivity extends AppCompatActivity {
#Inject
IAppDBHelper helper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MyApplication)getApplication()).getComponent()
.inject(this);
// It's OK
helper.getWritable().execSQL("XXX");
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
}
SecondActivity:
public class SecondActivity extends AppCompatActivity {
#Inject
IAppDBHelper helper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// nullPointer on helper
helper.getWritable().execSQL("XXX");
}
}
After injecting my AppComponent inside my SecondActivity, nullPointer error fixes but my question is do I have to inject My AppComponent every time I want inject IAppDbHelper? So what does #Singleton and injecting in inside my MainActivity mean? Doesn't should they inject that IAppDbHelper for my SecondActivity?
You will need to get your component and inject each class that Android creates. This typically means all Activities, Fragments, and Views. Dagger can't create those classes, so the only way Dagger knows about the externally-created instance is when you pass it into the inject method on your Component.
During the injection, all #Inject fields on MainActivity and SecondActivity will be provided, and any instances that Dagger creates will also have their dependencies injected, and so forth all the way down the line. This means that you won't usually need your Component directly outside of your externally-created classes like Activities and Fragments, or (of course) your Application instance where you create the Component instance itself.
#Singleton does mean that the instance will remain the same between MainActivity, SecondActivity, and any future instances of MainActivity or SecondActivity that Android may create as you background the app. However, you still need to request injection for those classes as Android creates them, in order to receive that same instance that you've guaranteed with #Singleton.
First of all , I am newbie, just starting to explore dagger, I have some problems with understanding, so hope someone can help me.
I have read a lot about dagger, but still cannot figure out some parts.
I created my ApplicationComponent and it looks like this
#Singleton
#Component(modules = {
ApplicationModule.class,
ThreadingModule.class,
NetworkModule.class,
DatabaseModule.class,
ServiceModule.class,
ParseModule.class,
PreferencesSessionModule.class})
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
void inject(MainAppActivity mainAppActivity);
void inject(BaseFragment baseFragment);
}
And it works great everything injects correctly, but now I wanna to dive deeper into dagger API and use Custom Scope
I have module called PermissionModule it is used for Android M versions.
#PerActivity
#Module
public class PermissionModule {
#Provides
#PerActivity
PermissionController providePermissionController(Activity activity) {
return new PermissionManager(activity);
}
}
And I want to it to be injected into my activity and be in the memory only when activity is also in memory (actvity lifecycle)
#PerActivity
#Component(modules = {
ActivityModule.class,
PermissionModule.class
})
public interface ActivityComponent {
Activity activity();
void inject(BaseActivity baseActivity);
PermissionModule permissionModule();
}
My ActivityComponent
#PerActivity
#Component(modules = {
ActivityModule.class,
PermissionModule.class
})
public interface ActivityComponent {
Activity activity();
void inject(BaseActivity baseActivity);
PermissionModule permissionModule();
}
And my BaseActivity
public abstract class BaseActivity extends AppCompatActivity implements SpiceManagerProvider, NetworkRequestsExecutor {
// Dependencies are injected by ApplicationComponent
#Inject
protected ApplicationSettingsManager mApplicationSettingsManager;
#Inject
protected SpiceManager mSpiceManager;
#Inject
protected ScheduledThreadPoolExecutor mPoolExecutor;
!!!!!!
Should be injected by activity component
#Inject
protected PermissionController mPermissionController;
And in onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
// Injecting dependencies
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().inject(this);
DaggerActivityComponent.builder().activityModule(new ActivityModule(this)).build().inject(this);
mPermissionController.requestPermission(Manifest.permission.ACCESS_FINE_LOCATION);
mPermissionController.requestPermission(Manifest.permission.CAMERA);
super.onCreate(savedInstanceState);
}
I got the error
PermissionController cannot be provided without an #Provides- or
#Produces-annotated method.
.ui.activities.base.BaseActivity.mPermissionController
What is wrong in my code ?
Also not to create new question and it is related to this topic.
How does dagger2 parse Scope annotation, I cannot figure out this. As I understand dagger only recognizes Singleton annotation and all other annotations doesn't affect dagger decision, because all other annotations will have scope of activity ?
so the problem is that you call the ApplicationComponent's inject method first
application.getApplicationComponent().inject(this);
which tries to inject all the members, including the PermissionController. But the ApplicationComponent can not provide this, and that is what Dagger complains about.
The solution is to only call the ActivityComponent's inject() method.
Most probably you do need dependencies provided by the ApplicationComponent at some point. To archive that, you'd need to combine the two components. Dagger provides two ways for that,subcomponents and component dependencies
When using component dependencies, rou would end up with something like this in your Activity's onCreate() method:
DaggerActivityComponent
.builder()
.applicationComponent(application.getApplicationComponent())
.activityModule(new ActivityModule(this))
.build().inject(this);
when you change your components to look something similar to this:
#PerActivity
#Component(
dependencies = ApplicationComponent.class,
modules = {
ActivityModule.class,
PermissionModule.class
}
)
public interface ActivityComponent {
...
}
note that you need to provide dependencies explicitly in the ApplicationComponent when you need it in the ActivityComponent (or any injectors)
If there is any dependency from parent component ( for instance #Componenet (model=AppModel.class) public interface appComponent... ) that you want to use in child component (#ActivityScope #Component (DEPENDENCY=APPCOMPONENT.class, model= ActivityModel.class) public interface activityComponenet... ) you need to expose it in parent component. Only exposed dependencies are accessible downstream (in child components). You do it by writing method from appModel that need to provide dependency downstream in appComponenet interface. Name of the method do not need to match the name of method in appModel, only the return type count.
About your confusion on Dagger scopes , I am hereby specifying some conclusions regarding scopes
Any time a un-scoped service is being injected by the same component, a new instance of a service is created.
First time a #Singleton scoped service is being injected, it is instantiated and cached inside the injecting component, and then the same exact instance will be used upon injection into other fields of the same type by the same component.
Custom user-defined scopes are functionally equivalent to a predefined #Singleton Scope
Injection of scoped services is thread safe.
If you really want to clearly understand how internally Dagger uses singleton and custom scope , follow this article Dagger 2 Scopes : How It Works Internally