I have a small question to Dagger2.
But first let me show the sample code:
#Singleton
#Component(module={ApplicationModule.class})
public Interface ApplicationComponent {
}
#Module
public class ApplicationModule {
#Provides
public Context provideContext() {
return context;
}
}
I know that the objects from the component now are "Singletons"..
My question... Did that have any effect to the Module? Is the Module also Singleton?
No, the Module will not be singleton unless you specify the scope for the #Provides annotated provider methods as well.
#Singleton
#Component(module={ApplicationModule.class})
public Interface ApplicationComponent {
Context context;
}
#Module
public class ApplicationModule {
#Provides //unscoped, every injection is new instance
public Context context() {
return context;
}
#Provides
#Singleton //scoped, one instance per component
public Something something() {
return new Something();
}
}
Related
I have created simple project with dagger2 and mvp.
This is my component :
#MainScope
#Component(modules = {MainModule.class})
public interface IMainComponent {
void inject(MainActivity mainActivity);
}
and This is MainModule.class:
#Module
public class MainModule {
#MainScope
#Provides
IMain.IMainModel model() {
return new MainModel();
}
}
Now in presenter i want to inject presenter from it's constructor so i do :
public class MainPresenter implements IMain.IMainPresenter {
IMain.IMainModel model;
IMain.IMainView view;
#Inject
public MainPresenter(IMain.IMainModel model) {
this.model = model;
}
But i got This error:
symbol: class DaggerIMainComponent
location: package com.safarayaneh.engineer.main.di
E:\Projects\Android\NewEng\Engineer\engineer\src\main\java\com\safarayaneh\engineer\main\di\IMainComponent.java:9: error: [Dagger/MissingBinding] com.safarayaneh.engineer.main.mvp.IMain.IMainPresenter cannot be provided without an #Provides-annotated method.
When make provider in MainModule.class to create presenter and remove #Inject above presenter constructor , everything is fine:
#Module
public class MainModule {
#MainScope
#Provides
IMain.IMainModel model() {
return new MainModel();
}
#MainScope
#Provides
IMain.IMainPresenter presenter(IMain.IMainModel model) {
return new MainPresenter(model);
}
}
Your problem is that your Activity expects IMain.IMainPresenter, but if you just annotate the constructor then what's placed on the objects graph is the concrete MainPresenter.
You've got three options here:
Use explicit provider method (as you did)
Use #Binds annotation inside the module to specify that MainPresenter should be provided as IMain.IMainPresenter
Don't use interface for the presenter
I'm new to Dagger2 and DI in general, but I'm interested to populate a map with injected keys/values. The problem is that it works if I provide the exact types, I can't make it work with wildcards, any solution for that?
#Module
public class SimpleIssueModule
{
#Provides
#Singleton
#IntoMap
#StringKey("simple_issue")
public SimpleIssue provideSimpleIssue()
{
return new SimpleIssue();
}
}
#Module
public class DaggerFactoryModule
{
#Provides
#Singleton
public Factory provideFactory(Map<String, Provider< ? extends Issue>> map)
{
return new Factory(map);
}
}
If you want a map of Provider< ? extends Issue>> map, then you need to use Issue as the type returned in your module. Dagger will not do any casting or guessing on its own.
#Provides
#Singleton
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue() {
return new SimpleIssue();
}
what to do in case I need a Module that provides a base class (Issue) into a Map and also need a provider of the concrete class (SimpleIssue) and I would like it to be Singleton (same instance returns in both cases)
In this case you provide the #Singleton of SimpleIssue.
#Provides
#Singleton
public SimpleIssue provideSimpleIssue() {
return new SimpleIssue();
}
// or you can use constructor injection, dropping the method above...
#Singleton
public class SimpleIssue {
#Inject
public SimpleIssue(...) {
}
}
Then you bind this instance into a Map. There is no need for a scope, since the implementation should declare it (as done above).
#Provides
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue(SimpleIssue issue) {
return issue;
}
// or alternatively with `#Binds` when using an abstract class / interface
// this leads to actually better performing dagger code
#Binds
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue(SimpleIssue issue);
I am exploring the new dagger.android from Dagger 2.11. I hope not to have to create custom scope annotation like #PerActivity. So far I was able to do the following:
1) Define Application scope Singletons and injecting them into activities.
2) Define Activity scope non-Singleton dependencies and injecting them into their activities using #ContributesAndroidInjector
What I cannot figure out is how to have an Application scope Singleton and Activity scope non-Singletons using it.
In the example below, I would like my Activity scope MyActivityDependencyA and MyActivityDependencyB to have access to a Singleton MyActivityService
The setup below results in:
Error:(24, 3) error: com.example.di.BuildersModule_BindMyActivity.MyActivitySubcomponent
(unscoped) may not reference scoped bindings:
#Singleton #Provides com.example.MyActivityService
com.example.MyActivitySingletonsModule.provideMyActivityService()
Here is my setup. Note, I defined separate MyActivitySingletonsModule and MyActivityModule since I could not mix Singleton and non-Singleton dependencies in the same Module file.
#Module
public abstract class BuildersModule {
#ContributesAndroidInjector(modules = {MyActivitySingletonsModule.class, MyActivityModule.class})
abstract MyActivity bindMyActivity();
}
}
and
#Module
public abstract class MyActivityModule {
#Provides
MyActivityDependencyA provideMyActivityDependencyA(MyActivityService myActivityService){
return new MyActivityDependencyA(myActivityService);
}
#Provides
MyActivityDependencyB provideMyActivityDependencyB(MyActivityService myActivityService) {
return new MyActivityDependencyB(myActivityService);
}
}
and
#Module
public abstract class MyActivitySingletonsModule {
#Singleton
#Provides
MyActivityService provideMyActivityService() {
return new MyActivityService();
}
}
and
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
BuildersModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(App application);
AppComponent build();
}
void inject(App app);
}
Is it even possible to do what I am trying to do without defining custom scope annotations?
Thanks in advance!
Why avoid custom scopes? Custom scopes are still required for the new dagger.android dependency injection framework introduced in Dagger 2.10+.
"My understanding is #ContributesAndroidInjector removes the need for custom annotation and I was able to prove it by using non-singletons defined in the activity scope without any issues."
#ContributesAndroidInjector (available in v2.11) does not remove the need for custom scopes. It merely replaces the need to declare #Subcomponent classes that does not make use of #Subcomponent.Builder to inject dependencies required by the component at runtime. Take a look at the below snippet from the official dagger.android user guide about #ContributesAndroidInjector;
"Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use #ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with #ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well."
#ActivityScope
#ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourActivityInjector();
The key here is "If the subcomponent needs scopes, apply the scope annotations to the method as well."
Take a look at the following code for an overview of how to use #Singleton, #PerActivity, #PerFragment, and #PerChildFragment custom scopes with the new dagger.android injection framework.
// Could also extend DaggerApplication instead of implementing HasActivityInjector
// App.java
public class App extends Application implements HasActivityInjector {
#Inject
AppDependency appDependency;
#Inject
DispatchingAndroidInjector<Activity> activityInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create().inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityInjector;
}
}
// AppModule.java
#Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
#PerActivity
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity mainActivityInjector();
}
// AppComponent.java
#Singleton
#Component(modules = AppModule.class)
interface AppComponent {
void inject(App app);
}
// Could also extend DaggerActivity instead of implementing HasFragmentInjector
// MainActivity.java
public final class MainActivity extends Activity implements HasFragmentInjector {
#Inject
AppDependency appDependency; // same object from App
#Inject
ActivityDependency activityDependency;
#Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
addFragment(R.id.fragment_container, new MainFragment());
}
}
#Override
public final AndroidInjector<Fragment> fragmentInjector() {
return fragmentInjector;
}
}
// MainActivityModule.java
#Module
public abstract class MainActivityModule {
#PerFragment
#ContributesAndroidInjector(modules = MainFragmentModule.class)
abstract MainFragment mainFragmentInjector();
}
// Could also extend DaggerFragment instead of implementing HasFragmentInjector
// MainFragment.java
public final class MainFragment extends Fragment implements HasFragmentInjector {
#Inject
AppDependency appDependency; // same object from App
#Inject
ActivityDependency activityDependency; // same object from MainActivity
#Inject
FragmentDependency fragmentDepency;
#Inject
DispatchingAndroidInjector<Fragment> childFragmentInjector;
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Perform injection here before M, L (API 22) and below because onAttach(Context)
// is not yet available at L.
AndroidInjection.inject(this);
}
super.onAttach(activity);
}
#Override
public void onAttach(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Perform injection here for M (API 23) due to deprecation of onAttach(Activity).
AndroidInjection.inject(this);
}
super.onAttach(context);
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_fragment, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
addChildFragment(R.id.child_fragment_container, new MainChildFragment());
}
}
#Override
public final AndroidInjector<Fragment> fragmentInjector() {
return childFragmentInjector;
}
}
// MainFragmentModule.java
#Module
public abstract class MainFragmentModule {
#PerChildFragment
#ContributesAndroidInjector(modules = MainChildFragmentModule.class)
abstract MainChildFragment mainChildFragmentInjector();
}
// MainChildFragment.java
public final class MainChildFragment extends Fragment {
#Inject
AppDependency appDependency; // same object from App
#Inject
ActivityDependency activityDependency; // same object from MainActivity
#Inject
FragmentDependency fragmentDepency; // same object from MainFragment
#Inject
ChildFragmentDependency childFragmentDepency;
#Override
public void onAttach(Context context) {
AndroidInjection.inject(this);
super.onAttach(context);
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_child_fragment, container, false);
}
}
// MainChildFragmentModule.java
#Module
public abstract class MainChildFragmentModule {
}
// PerActivity.java
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerActivity {
}
// PerFragment.java
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerFragment {
}
// PerChildFragment.java
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerChildFragment {
}
// AppDependency.java
#Singleton
public final class AppDependency {
#Inject
AppDependency() {
}
}
// ActivityDependency.java
#PerActivity
public final class ActivityDependency {
#Inject
ActivityDependency() {
}
}
// FragmentDependency.java
#PerFragment
public final class FragmentDependency {
#Inject
FragmentDependency() {
}
}
// ChildFragmentDependency.java
#PerChildFragment
public final class ChildFragmentDependency {
#Inject
ChildFragmentDependency() {
}
}
For a complete dagger.android 2.11 setup guide using #ContributesAndroidInjector and custom scopes mentioned above, read this article.
There are some problems here: firstly, ActivitySingleton doesn't make much sense. A dependency is either a singleton (per app, or app scoped) or not.
If it is not a singleton it could be activity scoped (per activity). This would mean it lived and died with the Activity i.e., that its lifecycle was congruent with that of the Activity itself and hence it would be destroyed with the onDestroy of the Activity.
That doesn't mean that everything that is injected inside an Activity must be #PerActivity. You can still inject #Singleton dependencies there (like per app OkHttpClient for instance). However, these #Singleton dependencies will not be bound in the module set that composes a #PerActivity component. Instead, they will be bound in the module set for parent components and obtained through the component hierarchy (dependent components or sub-components).
These means that your ActivitySingletonsModule is incorrect, see the comments in the code below:
#Module
public abstract class MyActivitySingletonsModule {
//#Singleton
//^^ remove the annotation here if you want to use the
//in your ActivityComponent
//If you need this as a per-app singleton, then include
//this module at the AppComponent level
#Provides
MyActivityService provideMyActivityService() {
return new MyActivityService();
}
}
I do not understand the reluctance to define a custom scope. These are extremely lightweight and can improve readability. Here is the single line of code you would need to create a #PerActivity scope.
#Scope #Retention(RetentionPolicy.RUNTIME) public #interface PerActivity {}
I suspect the concept of scopes is unclear and this is leading to the reluctance. Admittedly, it can be rather confusing. However there are some really good canonical answers that help clarify. I would suggest this question as a start:
Dagger2 Custom Scopes : How do custom-scopes (#ActivityScope) actually work?
If there is #inject, then it means there must be #provide?
inject field gets its value from #provide method of module?
Yes if you use Module
#Module
public class SomeModule {
#Provides
Unscoped unscoped() {
return new Unscoped();
}
#Provides
#Singleton
Scoped scoped() {
return Scoped();
}
}
BUT classes with #Inject constructor get automatically appended to your scoped component even if no module is specified for it:
#Singleton
public class Scoped {
#Inject
public Scoped() {
}
}
public class Unscoped {
#Inject
public Unscoped() {
}
}
If there is #Inject annotation then it's dependency can be provided in two ways :
By Using Provides annotation in module
#Provides
TasksPresenter provide TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
return new TasksPresenter(tasksRepository,tasksView);
}
By Using Constructor Injection
#Inject
TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
mTasksRepository = tasksRepository;
mTasksView = tasksView;
}
One thing to observe here is Constructor Injection solve two thing
Instantiate object
Provides the object by adding it to Object graph.
I'm struggling with dagger 2 in order to understand how i can pass a context or another according to my needs.
- First I have an ApplicationModule annotated #Singleton since it provides hi level objects like the webservice object, the model ..., generally those objects are passed the ApplicationContext (since the y need to live during the whole Application lifetime)
#Singleton
#dagger.Component(modules = {
AppModule.class
})
public interface AppComponent {
void inject(MyApp application);
Model model();
Context context();<--- should provide the application Context for the Object above (model)
...
the implementation looks like that
#dagger.Module
public class AppModule {
private final Application app;
public ApplModule(Application app) {
this.app = app;
}
#Provides
#Singleton
Model provideModel(Bus bus) {
return new Model(bus);
}
#Provides
#Singleton
Context provideApplicationContext() {
return app.getApplicationContext();
}
...
secondly I have an Activity Scope componenet in with I provide the current activity and different views which need a Context.
#ActivityScope
#Component(
dependencies = AppComponent.class
, modules = {ActivityModule.class}
)
public interface ActivityComponent {
void inject(MyActivity activity);
Context context(); <---should provide the activity's context
MyView homeView(); <----takes a Context as a contructor parameter
#Module
public class ActivityModule {
private final Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#Provides
#ActivityScope
public Activity activity() {
return activity;
}
#Provides
#ActivityScope
#Named("viewcontext") <----- if I removed this I get an error from Dagger
public Context context() {
return activity;
}
#Provides
#ActivityScope
MyView provideZeView(Bus bus, Model model) { <---- previously receiving the ApplicationContext as a parameter
MyView v = new MyView(activity, bus, model); <---- must pass the activity otherwise passing the Context reveived is the ApplicationContext
return v;
}
so Here are my questions:
I used scopes in order to have a better "granularity" over what is passed and i still get the applicationContext
If I remove the #Named qulifier i get an error
previously the Views where produced by another module with a dependence to the ActivityModule but still getting the ApplicationContext
Well the point is I am certainly missing something...but I can't figure what, maybe I misundestood the use of Scopes
you can use qualifiers like this. in two separate files define the following:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityContext {
}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface ApplicationContext {
}
then in your ActivityModule do this:
#Provides
#ActivityScope
#ActivityContext
public Context context() {
return activity;
}
and likewise in your appmodule do this:
#Provides
#Singleton
#ApplicationContext
Context provideApplicationContext() {
return app.getApplicationContext();
}
now we have a way to ask for whatever type of context we need based on the qualifier #ApplicationContext and #ActivityContext.
so for example in your activity you could do this:
#Inject #ApplicationContext
Context c;
which would inject an application context.
and in a module you could do this for example:
#Provides
#ActivityScope
LoginPresenter provideLoginPresenter(#ActivityContext Context context) {
return new LoginPresenter(context);
}
to provide an activity context. this is just an example.
#Named is a requirement if you want to provide multiple objects of the same type from your module.
As far as your second question, in regards to passing the correct Activity context, you need to have this in your ActivityComponent:
Activity activity();