Injecting from many scopes at Dagger 2 - android

I want to inject objects from two scopes at the same class.
e-g. I am implementing MVP. As a view I have a fragment. And in those fragment I want to inject:
presenter from ApplicationScope (I use Singleton)
derivatives of RecyclerView.Adapter and LayoutManager from ActivityScope.
Is it possible? What are the best practise to achieve this?

Yes, it's definitely possible. There are probably different ways to accomplish that.
It's straightforward as long as each component specifies a scope. For example, if your application-level dependencies are #Singleton then your activity-level component also needs a #Scope like #ActivityScope...
For example: application/singleton scope component:
#Singleton
#Component
public interface ApplicationComponent {
MainPresenter getMainPresenter();
}
Activity module:
#Module
public class MainModule {
private final Activity mActivity;
public MainModule(Activity activity) {
mActivity = activity;
}
#Provides
Context provideContext() {
return mActivity;
}
// and other activity-specific dependencies here...
}
and matching activity-level component:
#ActivityScope
#Component(dependencies = ApplicationComponent.class, modules = MainModule.class)
public interface MainComponent {
void initialize(MainActivityFragment mainActivityFragment);
}
then it all just works... for example you can inject both the singletons and activity-scope dependencies into the fragment:
final MainComponent mainComponent = DaggerMainComponent.builder()
.applicationComponent(((App) getActivity().getApplication()).getComponent())
.mainModule(new MainModule(getActivity()))
.build();
...
mainComponent.initialize(this);
See the full working code here.
PS. I'm not so sure it's best to make the presenters singletons though :-) For one reason, I'd expect to inject activity-level dependencies into the presenter, but this way it won't be possible... Let me know how this works for you and if you find another approach to retain the presenters.

Related

Dagger2 and Android

I am trying to implement Dagger Dependency Injection into my app but I am having a hard time understanding how it works, especially coming from Spring where DI was much easier and much more declarative.
What I want to do is have a bunch of inject-ready objects that can be used throughout my app, that is the SharedPreferences, the Network objects (OkHttp, Retrofit, Picasso...), and EventBus and a SchedulerProvider object for RxJava.
This sample seems to offer everything I need but I am having trouble grasping some concepts.
In this other sample referenced in the previous page they create a GithubService that uses the Retrofit object provided in the NetModule. For that they create a GithubComponent like this:
#UserScope
#Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
void inject(MainActivity activity);
}
They are using a UserScope annotation that defines its own scope. Since #Singleton cannot be used, does this mean that the object will not be a Singleton? How do scopes really affect the DI? It seems they are only declaring a named-scope with no more effect, but I'm not sure.
Also, my app is built using Activities with Fragments. Do I have to create a Component for every Fragment in my app? i.e. I need to use my REST api services all throughout the app, do I have to declare a Component for every screen using them? This raises the amount of boilerplate code required and thus sounds not very clean.
Components ought to be the "big DI provider" that provides everything for a specific scope.
For example, you could have a SingletonComponent with #Singleton scope that has every single module added to it that has at least one #Singleton scoped provider method.
#Singleton
#Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
// provision methods
OkHttpClient okHttpClient();
RealmHolder realmHolder();
// etc.
}
You can have the provision methods defined per module.
public interface DatabaseComponent {
RealmHolder realmHolder();
}
public interface NetworkingComponent{
OkHttpClient okHttpClient();
}
In which case you'd have
#Singleton
#Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent
extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
// provision methods inherited
}
In a Module, you can specify a factory method ("provider method") that specifies how to create a particular type of dependency.
For example,
#Module
public class NetworkingModule {
#Provides
#Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}
#Provides
#Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}
You can imagine the #Singleton scope as the big DI container that Spring would have given you.
You can also provide instances of the class using #Inject annotated constructor. This can receive any class from the component that is able to instantiate it from provider methods within that scoped component's modules (and unscoped dependencies, of course).
#Singleton
public class MyMapper {
#Inject
public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
}
}
or
#Singleton
public class MyMapper {
#Inject
RealmHolder realmHolder;
#Inject
OkHttpClient okHttpClient;
#Inject
public MyMapper() {
}
}
Then this will be available in the component, you can even make provision method for it to make it inheritable in component dependencies:
#Singleton
#Component(modules={...})
public interface SingletonComponent {
MyMapper myMapper();
}
With Dagger2, you can also additionally create "subscoped components", that inherit all dependencies provided from a component of a given scope.
For example, you can inherit all #Singleton scoped components, but you can still have new scoped dependencies per that new scope, such as #ActivityScope.
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
Then, you can create subscoped components using either subcomponents, or component dependencies.
Subcomponent:
.
#ActivityScope
#Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
MainPresenter mainPresenter();
}
Then this can be created in its parent scoped component:
#Singleton
#Component(modules={...})
public interface SingletonComponent {
MainActivityComponent mainActivityComponent(MainActivityModule module);
}
Then you can use the singleton component to instantiate this:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));
Component dependency:
.
#ActivityScope
#Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
MainPresenter mainPresenter();
}
For this to work, you must specify provision methods in the superscoped component.
Then you can instantiate this like so:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(singletonComponent)
.mainActivityModule(new MainActivityModule(mainActivityHolder))
.build();
In Dagger2, you can therefore obtain dependencies either via:
#Inject annotated constructor parameters
#Inject annotated fields on classes with #Inject annotated constructor
from #Component provision methods
via manual field injection method defined in component (for classes you can't create using #Inject annotated constructor)
Manual field injection can happen for classes like MainActivity, that you yourself don't create.
Manual field injection injects only the specific class that you are injecting. Base-classes don't get automatically injected, they need to call .inject(this) on component.
It works like this:
#ActivityScope
#Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
Then you can do:
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(getSingletonComponent())
.mainActivityModule(new MainActivityModule(this))
.build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
mainActivityComponent.inject(this);
}
}
1)
Since #Singleton cannot be used, does this mean that the object will
not be a Singleton?
GitHubComponent component has #UserScope scope, it should be #Singleton if you want to declare Singletons in this scope.
2)
How do scopes really affect the DI? It seems they are only declaring a
named-scope with no more effect, but I'm not sure.
From javax docs
A scope annotation applies to a class containing an injectable
constructor and governs how the injector reuses instances of the type.
By default, if no scope annotation is present, the injector creates an
instance (by injecting the type's constructor), uses the instance for
one injection, and then forgets it. If a scope annotation is present,
the injector may retain the instance for possible reuse in a later
injection.
3)
Do I have to create a Component for every Fragment in my app?
You can create it once, instantiate and store in your Application object and then query it everytime you need to inject something if your fragment or activity.
Check this example

Dagger 2 Scopes explanation

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

How inject the Activity to object that is being injected to the Activity?

I have this module:
#Module
public class UserProfileModule {
#Provides
#Singleton
UserProfileController providesUserProfileController() {
return new UserProfileController();
}
}
and this component:
#Component(modules = {UserProfileModule.class})
#Singleton
public interface AppComponent {
void inject(UserProfileActivity activity);
}
So far, in My UserProfileActivity I can #Injectan UserProfileController. But now, I need to inject the UserProfileActivity to the controller. I mean, inject each other.
I could do it by calling a UserProfileController setter in UserProfileActivity: setActivity(this);, but it would be nice if can be automatic.
How can achieve that?
Thanks.
For starters: add it to the constructor. Then declare that dependency.
#Provides
#Singleton
UserProfileController providesUserProfileController(UserProfileActivity activity) {
return new UserProfileController(activity);
}
After doing so dagger will complain about not being able to provide UserProfileActivity unless you already do so. If you don't, add another module, or just provide the dependency from that same module. The actual implementation follows, first we need to fix your code.
#Singleton is a dependency on top of the hierarchy. You can't—or at least should not—have an activity dependency for a #Singleton annotated object, since this will probably cause bad smells and/or memory leaks. Introduce a custom scope #PerActivity to use for dependencies within your activities lifespan.
#Scope
#Retention(RUNTIME)
public #interface PerActivity {}
This will allow for correct scoping of the object. Please also refer to some tutorials about dagger, since this is a really important issue and covering everything in a single answer would be too much. e.g. Tasting dagger 2 on android
The following uses the latter approach of the aforementioned 2 options by expanding your module:
#Module
public class UserProfileModule {
private final UserProfileActivity mActivity;
public UserProfileModule(UserProfileActivity activity) {
mActivity = activity;
}
#Provides
#PerActivity
UserProfileActivity provideActivity() {
return mActivity;
}
#Provides // as before
#PerActivity
UserProfileController providesUserProfileController(UserProfileActivity activity) {
return new UserProfileController(activity);
}
}
If you now use your component Builder you can create a new instance of your module with the activity as an argument. The dependency will then correctly be supplied.

Dagger 2 error: dependency "cannot be provided without an #Inject constructor" while it actually annotated with #Inject

I've started using Dagger 2 and faced strange issue that looks like a bug to me.
I have 3 modules, that are composed into one subcomponent, which in turn extends/pluses higher level component.
Subcomponent is pretty simple: just combination of modules and a single injection point:
#Singleton
#Subcomponent(
modules = {
NavigationDrawerModule.class,
NavigationListModule.class,
SwitcherModule.class
}
)
public interface NavigationDrawerComponent {
NavigationDrawerFragment inject(NavigationDrawerFragment object);
}
First modules looks like this - it provides general fragment-level dependencies:
#Module
public class NavigationDrawerModule {
private final Activity activity;
private final View rootView;
private final LoaderManager loaderManager;
public NavigationDrawerModule(Activity activity, View rootView, LoaderManager loaderManager) {
this.activity = activity;
this.rootView = rootView;
this.loaderManager = loaderManager;
}
#Provides #Singleton EventBus provideLocalBus() {
return EventBus.builder().build();
}
#Provides #Singleton View provideViewRoot() {
return rootView;
}
#Provides #Singleton LoaderManager provideLoaderManager() {
return loaderManager;
}
#Provides #Singleton Context provideContext() {
return activity;
}
}
Second module looks like this - it provides presenter/controller and their dependencies for a subset of UI on screen:
#Module
public class SwitcherModule {
#Provides SwitchController provideSwitcherController(SwitchControllerImpl impl) {
return impl;
}
#Provides SwitcherView provideSwitcherView(SwitcherViewImpl impl) {
return impl;
}
}
Third module - another presenter/controller for a subset of UI:
#Module
public class NavigationListModule {
#Provides #Singleton NavigationListController provideNavigationListController(NavigationListControllerImpl impl) {
return impl;
}
#Provides #Singleton NavigationListView provideNavigationListView(NavigationListViewImpl impl) {
return impl;
}
}
Relevant part of the fragment that is being injected:
#Inject SwitchController identitySwitchController;
#Inject SwitcherView identitySwitcherView;
#Inject NavigationListController navigationListController;
#Inject NavigationListView navigationListView;
NavigationListControllerImpl implements the following constructor:
#Inject
public NavigationListControllerImpl(Context ctx, EventBus bus) {
this.ctx = ctx;
this.bus = bus;
}
Error I'm getting from the Dagger 2 compiler is the following:
error: ...sidenavigation.navigationlist.NavigationListControllerImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method.
...sidenavigation.NavigationDrawerFragment.navigationListController
[injected field of type: ...sidenavigation.navigationlist.NavigationListController navigationListController]
...sidenavigation.navigationlist.NavigationListModule.provideNavigationListController(...sidenavigation.navigationlist.NavigationListControllerImpl impl)
[parameter: ...sidenavigation.navigationlist.NavigationListControllerImpl impl]
Error complains about missing #Inject-annotated constructor, but it exists! If I replace implicit NavigationListControllerImpl instance creation (passing via #Provides-method parameter) with explicit (with new), dagger starts complaining about the same error but now for the presenter object which is the second entry in the same module, and so on.
All this situation looks very strange, and I'd like to hear some input from more experienced Dagger 2 users (and developers?).
Thank you in advance!
I got this same error because I forgot to expose the objects provided by the modules in the parent component to the other components that are depend on it.
Parent component example:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
AppPref exposeAppPref(); /* my issue was caused by forgot this line,
the method name doesn't matter, what matters is the object type AppPref provided in the AppModule
that you want it to be available in the component that declares this component as one of its dependencies*/
}
Sample component that makes the above component as a dependency
#UserScope
#Component (dependencies = {AppComponent.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
Update:
AppModule:
...
#Provides
#Singleton
AppPref provideAppPref() {
return appPref;
}
...
The GlobalComponent and the subcomponent NavigationDrawerComponent must have different scopes. Use #Singleton for your GlobalComponent and some another scope for the subcomponent.
Otherwise, if you apply the same scope to the GlobalComponent and to the subcomponent, you must declare the modules of your subcomponent in your global component as well:
#Component(
// modules from subcomponent must be declared here also
modules = {NavigationListModule.class,
SwitcherModule.class,
NavigationDrawerModule.class,
...}
)
#Singleton
public interface GlobalComponent {
NavigationDrawerComponent plus(NavigationDrawerModule module);
}
For your use case, you can also use component dependencies. For instance:
#Component(
dependencies = GlobalComponent.class,
modules = {NavigationListModule.class,
SwitcherModule.class,
NavigationDrawerModule.class}
)
#YourOtherDaggerScope // #Singleton scope won't work here, it must be a different scope
public interface NavigationDrawerComponent extends GlobalComponent { // extend the parent component if you wish to get access to parent dependencies
NavigationDrawerFragment inject(NavigationDrawerFragment object);
}
Seems like I've figured out what was wrong with my Dagger 2 setup. It's not possible to use the same scope in both component and subcomponents. It's required to define a new scope for subcomponent. In my case I've ended up creating #Screen scope for me subcomponent.
I'd say that this is a small but very annoying defect in Dagger 2. Apparently dagger-compiler reports nice and understandable error about the same scopes in a parent component and child component if child component is extended with a parent component as dependency. But completely misleading error is reported by the compiler if parent component and child subcomponent share the same scope.
Thank you, #lukas, for giving me a hint here https://stackoverflow.com/a/30383088/808313 that led to a problem resolution.
Came accross this issue today too. For me there was a problem with the Annotation processing (on Android Studio 2.2 with gradle 2.x).
Instead of ~~apt~~ I used annotationProcessor
I used
annotationProcessor 'com.google.dagger:dagger-compiler:2.6'
and now It's working.
Came across the same issue while trying to create Subcomponents, but it seems to be fixed in Dagger 2.0.1.
Seems it is the same kinda error dagger reports for many mistakes. In my case, my target injection was expecting concrete class (Presenter) where as the module that provides presenter was returning only the interface (DemoContract.Presenter)
So changed from
#Inject
public Presenter mDemoPresenter;
to
#Inject
public DemoContract.Presenter mDemoPresenter;
and module that provides presenter looks like this:
#Module
public class DiDemoPresenterModule {
private final DemoContract.View mView;
DiDemoPresenterModule(MainActivity mView) {
this.mView = mView;
}
#Provides
public DemoContract.Presenter providesDemoPresenter(Repository repository) {
return new DemoPresenter(repository, mView);
}
}

Dagger 2 - modules from different components

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();
}

Categories

Resources