Dagger 2 Custom Scope for each Fragment (or Activity etc...) - android

I've looked at a couple different articles which seem to suggest two different ways of doing custom scoping in Dagger 2:
MVP Presenters that Survive Configuration Changes Part-2 (Github repo):
Uses unique custom scopes for each fragment, e.g. #Hello1Scope and #Hello2Scope for Hello1Fragment and Hello2Fragment respectively
Tasting Dagger 2 on Android:
Uses a single custom scope for all fragments, e.g. #PerFragment.
From what I understand, it seems that, as in method 2, it should be okay to have a single scope defined that can be used for all fragments (i.e., #PerFragment). In fact (please correct me if I'm wrong), it seems like the name of the custom scope is irrelevant, and it's only where the subcomponent is created (i.e. in Application, Activity, or Fragment) that matters.
Is there any use case for defining a unique scope for each fragment such as in case 1?

After reading the answer by #vaughandroid, and What determines the lifecycle of a component (object graph) in Dagger 2? I think I understand custom scopes well enough to answer my own question.
First, here are a couple rules when dealing with components, modules, and scoping annotations in dagger2.
A Component must have a (single) scope annotation (e.g. #Singleton or #CustomScope).
A Module does not have a scope annotation.
A Module Method may have a (single) scope that matches its Component or no scope, where:
Scoped: means a single instance is created for each instance of the component.
Unscoped: mean a new instance is created with each inject() or provider call
NOTE: Dagger2 reserves #Singleton for the root Component (and it's modules) only. Subcomponents must use a custom scope, but the functionality of that scope is exactly the same as #Singleton.
Now, to answer the question: I would say create a new named scope for each conceptually different scope. For example, create a #PerActivity, #PerFragment, or #PerView annotation that indicates where the component should be instantiated, and thus indicating its lifetime.
Note: this is a compromise between two extremes. Consider the case of a root component and n subcomponents you will need:
at least 2 annotations (#Singleton and #SubSingleton), and
at most n+1 annotations (#Singleton, #SubSingleton1, ... #SubSingletonN).
Example:
Application:
/** AppComponent.java **/
#Singleton
#Component( modules = AppModule.class )
public interface AppComponent{
void inject(MainActivity mainActivity);
}
/** AppModule.java **/
#Module
public class AppModule{
private App app;
public AppModule(App app){
this.app = app;
}
// For singleton objects, annotate with same scope as component, i.e. #Singleton
#Provides #Singleton public App provideApp() { return app; }
#Provides #Singleton public EventBus provideBus() { return EventBus.getDefault(); }
}
Fragment:
/** Fragment1Component.java **/
#PerFragment
#Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
public interface Fragment1Component {
void inject(Fragment1 fragment1);
}
/** Fragment1Module.java **/
#Module
public class Fragment1Module {
// For singleton objects, annotate with same scope as component, i.e. #PerFragment
#Provides #PerFragment public Fragment1Presenter providePresenter(){
return new Fragment1Presenter();
}
}
/** PerFragment.java **/
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerFragment {}

Your understanding is correct. The named scopes allow you to communicate intention, but they all work the same way.
For scoped provider methods, each Component instance will create 1 instance of the provided object.
For unscoped provider methods, each Component instance will create a new instance of the provided object whenever it needs to inject it.
The lifetime of the Component instance is important though. 2 different instances of the same component will provide different object instances, even scoped ones.
Scope names should indicate the lifetime of the provided object (which matches that of the Component instance) so #PerFragment makes much more sense to me.
From a quick look at the "MVP Presenters..." tutorial, it's not clear to me exactly what the author's intention is with having separate scopes. Since the names are just throwaway ones, I wouldn't read too much into it.

Related

How inject dagger non empty constructor module dependencies to hilt

Hilt is not supportting non epmty constructor modules. If we need to migrate partially to Hilt from dagger , how we can inject dependencies from legacy dagger modules having non empty constrctors to hilt components such as HiltViewModel.
// Legacy Dagger module
#Module
public class DaggerModule {
private final Boolean customBoolean;
DaggerModule(Boolean customBoolean) {
this.customBoolean = customBoolean;
}
#Provides
#Singleton
CustomClass provideCustomClass() {
return CustomClass(customBoolean);
}
}
#Module
public class AnotherDaggerModule {
#Provides
#Singleton
AnotherClassDepndsOnCustomClass provideAnotherClass(CustomClass customClass) {
return AnotherClassDepndsOnCustomClass(customClass);
}
}
// Migrated Hilt module
#HiltViewModel
class HiltViewModel #Inject constructor(
  private val anotherClass: AnotherClassDepndsOnCustomClass
) : ViewModel() {
  ...
}
Since we are not using components to pass some custom parameters while initialising modules, is there any solution which I'm not aware already exists?
While running the app, the app crashing with error DaggerModule must be set.
You'll need to refactor.
The documentation on Migrating to Hilt describes this case under the heading "Handling Component Arguments", since instantiable modules would otherwise be treated as component arguments passed through a Builder or Factory:
Hilt components cannot take component arguments because the initialization of the component is hidden from users. [...]
If your component has any other arguments either through module instances passed to the builder or #BindsInstance, read this section on handling those. Once you handle those, you can just remove your #Component.Builder interface as it will be unused.
Under "Component arguments" the Hilt documentation confirms that a refactor is required:
Because component instantiation is hidden when using Hilt, it is not possible to add in your own component arguments with either module instances or #BindsInstance calls. If you have these in your component, you’ll need to refactor your code away from using these.
You can consider some of these structures:
Replacement module / subclassing Modules
In your example, you might need to create a replacement Module that provides a binding for CustomClass. This might be as straightforward as subclassing the Module and providing a public no-arg constructor that provides the super(value) constructor call your module needs. If your Module would only ever get a single value in your graph (but might get a different value in a separate application), then this might be enough.
#Module
public class AdaptedDaggerModule extends DaggerModule {
AdaptedDaggerModule() {
super(true);
}
}
Note that module subclasses are somewhat limited in utility, and should not be used for testing overrides.
Custom subcomponents
However, you also wrote "components are created in respective modules where we need to use" in a comment, and you can continue doing so using custom subcomponents in Hilt, with more comprehensive documentation in javadoc or the main Dagger subcomponent documentation. Because you would create this component through an explicit call to a Builder or Factory, you could provide the Module instance there. Subcomponents inherit bindings from their parent components, so you could avoid specifying your entire list of Modules.
Note that doing this as a subcomponent is mostly valuable when you have a dense tree with multiple references to the instance you're providing in the constructor. If this is simply a matter of combining graph-based constructor arguments with one-off constructor arguments, assisted injection is probably a better option.
/** Subcomponents are usually declared on modules. You can also reuse one you have. */
#Module(subcomponents={YourSubcomponent.class})
public interface IncludeThisInYourHiltModuleList {}
#Subcomponent(modules={DaggerModule.class, AnotherDaggerModule.class})
public interface YourSubcomponent {
AnotherClassDepndsOnCustomClass anotherClass();
#Subcomponent.Builder
interface Builder {
Builder daggerModule(DaggerModule daggerModule); // arbitrary name
YourSubcomponent build(); // arbitrary name
}
}
#HiltViewModel
class HiltViewModel #Inject constructor(
private val yourSubcomponentBuilder: YourSubcomponent.Builder
) : ViewModel() {
fun yourMethod() {
val subcomponent =
yourSubcomponentBuilder.daggerModule(DaggerModule(false)).build()
val anotherClass = subcomponent.anotherClass()
// ...
}
}
Constructor values in Hilt-managed components
The most difficult case would be where your Module would want separate values in each of your Hilt-managed components, e.g. each Activity needing to pass a different constructor argument. In that case you might need to rephrase the customBoolean (or other parameters) as deriving the value from the Activity instance itself. This maintains Hilt's expectation that it can create an Activity component for each Activity instance that Android unpredictably creates or recreates, and it can do so without specifying any other constructor parameters.

Can the body of a Component be empty in Dagger2?

I'm learning Dagger and I'm just curious. I understand that if I plan to use Field Injection, I must have something like this:
#Singleton
#Component(modules={AppModule.class, NetModule.class})
public interface NetComponent {
void inject(MainActivity activity);
// void inject(MyFragment fragment);
}
where I list the classes that will have the field injection.
And also if this component is a parent of other components, I must have something like:
#Component(modules={ServiceModule.class,
ContextModule.class,
SchedulerModule.class})
#Singleton
public interface SingletonComponent {
Context appContext();
CatTable catTable();
CatDao catDao();
CatMapper catMapper();
CatRepository catRepository();
CatService catService();
Retrofit retrofit();
DatabaseManager databaseManager();
#Named("MAIN") Scheduler mainThreadScheduler();
#Named("BACKGROUND") Scheduler backgroundThreadScheduler();
}
But if I don't intend to do either one, can I have something like:
#Singleton
#Component(modules = arrayOf(DatabaseModule::class, AppModule::class,
ApiServiceModule::class))
interface AppComponent{
}
where the body is just empty.
(I'm aware that I will probably need field injection or child components, I just want to be sure that I'm not missing anything).
An entirely empty component (that does not extend another interface) will be useless. Components define entry points into the object graph, so without any methods at all, there's nothing you could do with the object. You've listed members injection methods, but you can also include subcomponent factory methods and builders and component provision methods.
ObjectA getA(); // component provision method
void inject(B b); // members injection method, which can return void
C inject(C c); // ...or return the object it injects, for convenience
// You can also make a subcomponent getter, if you supply necessary module instances...
DSubcomponent createD(SomeModule module);
// or just return a builder.
ESubcomponent.Builder createEBuilder();
This is especially important to remember because Dagger will only generate the code for graph bindings accessible from your Component interface. If you have a #Binds or #Provides for ObjectF, ObjectG, or ObjectH, those will not be a part of your Dagger implementation unless they are transitively reachable from the methods you put onto the component interface.
What about components-with-dependencies and subcomponents? A ChildComponent that depends on a ParentComponent will only get access to the objects available through "component provision methods" (zero-arg getters), so a component without methods is still useless. You can also supply an interface (without annotations) and then have your ParentComponent (or any other implementation) passed into the builder. In any case, the available objects are those listed on the parent component's public interface, which still precludes the interface from being empty.
Subcomponents, unlike components-with-dependencies, have their code generated at the same time as the their parents. This means that you don't have to be explicit about what is being inherited from the parent; Dagger can infer what is available in the parent graph. However, as part of their parent, subcomponents also need a reference from the parent component's interface, or else they won't be generated. (This reference can be a factory method that returns the subcomponent, a method that returns the Subcomponent's Builder, or a transitive injection from some other object or members injection on the Component.) Thus, even in the subcomponent case, an empty component is still useless.

Android | Dagger 2. Injecting different subclasses into Fragment depending of a condition

I am working with MVP and Dagger 2 DI. I have a Fragment that I reuse in a few activities. I have an interface type for presenter as a property of the Fragment, say MVPPresenter. Depending in which activity the Fragment is being used, I need to inject different presenters into it (each presenter is an implementation of MVPPresenter). So I need a way to inject each implementation of MVPPresenter into the Fragment as I need.
Currently, I have a terrible solution, which works, but it is simply wrong and creates unnecessary objects that are never used. Here is the code:
public class MyFragment {
...
#Inject
public void setPresenter(#NonNull ProfilePresenter presenter) {
if (mAdapter instanceof ProfileAdapter) {
this.presenter = presenter;
}
}
#Inject
public void setPresenter(#NonNull ContactsPresenter presenter) {
if (mAdapter instanceof ContactsAdapter) {
this.presenter = presenter;
}
}
...
}
Here is my Module:
#Module
class PresentersModule {
#Provides
#Singleton
ProfilePresenter ProfilePresenter() {
return new ProfilePresenter();
}
#Provides
#Singleton
ContactsPresenter ContactsPresenter() {
return new ContactsPresenter();
}
}
You see, depending on Adapter type, I assign presenter, or do not. I know this is stupid and all. Problem is that Dagger needs exact type to inject to be specified and Interface type wont work.
What is the proper way of dealing with such cases?
You have, as I see it, three solutions of varying degrees of weight.
Inject two choices as you have now: If you know all of your Fragment's use-cases up front, and you don't need to vary the dependency graphs any more than on a single class, you can do so easily using a similar method to what you have now. My variant uses Providers, which are bound automatically for any object in your graph, so that you don't unnecessarily create whole trees of objects; also, #Inject methods can take an arbitrary parameter list, so you can do all of your method injection in one method if you choose.
#Inject
public void setPresenter(
#NonNull Provider<ContactsPresenter> contactsPresenterProvider,
#NonNull Provider<ProfilePresenter> profilePresenterProvider) {
if (mAdapter instanceof ContactsAdapter) {
this.presenter = contactsPresenterProvider.get();
} else if (mAdapter instanceof ProfileAdapter) {
this.presenter = profilePresenterProvider.get();
}
}
The other two solutions involve multiple components: Instead of saying "there is one way of binding my graph together", you're effectively asking Dagger to generate multiple options for you, which means that your graphs can vary widely but stay consistent. This technique might be more useful if you reuse objects in different ways for different sections of your application, like if you have a Profile section and a Contacts section, each of which using a common A injecting a common B injecting a common C injecting a different D. To consistently support two deep graphs like that, child components are a much better option.
Use component dependencies: As in rst's answer, you can use component dependencies to isolate your fragments. They did a pretty good job of explaining, so I'll not repeat that here. You should be aware, though, that component dependencies can only consume bindings that are exposed on the component you depend on: Even if Foo and Bar are bound on DiComponent, you won't be able to access them from your ProfileComponent or ContactsComponent unless you put Foo getFoo() and Bar getBar() on your DiComponent. (That said, component dependencies don't have to be Dagger components, either; they can be arbitrary types that you implement yourself or let Dagger implement for you.)
Use subcomponents: Though rst alluded to subcomponents, I think they warrant a bit more explaining, particularly because they are a core component of the recently-released dagger.android functionality, and because Fragments and other UI pieces can be difficult to extract with component dependencies—subcomponents implicitly and automatically inherit bindings from the surrounding component, so you don't have to explicitly expose bindings on your DiComponent. See other differences at this SO question.
#Component
public interface DiComponent {
ProfileComponent getProfileComponent(); // Dagger generates implementations
ContactsComponent getContactsComponent(); // as part of DiComponent.
}
#Subcomponent(modules={ContactsModule.class})
public interface ContactsComponent {
void inject(MyFragment myFragment);
}
#Module
public interface ContactsModule {
#Binds MvpPresenter bindMvpPresenter(ContactsPresenter contactsPresenter);
}
#Subcomponent(modules={ProfileModule.class})
public interface ProfileComponent {
void inject(MyFragment myFragment);
}
#Module
public interface ProfileModule {
#Binds MvpPresenter bindMvpPresenter(ProfilePresenter profilePresenter);
}
In the above, the root DiComponent doesn't have a binding for MvpPresenter, so in itself it can't inject MyFragment. However, ProfileComponent and ContactsComponent can, and each will use different graphs configured in the corresponding Modules (but silently inheriting common bindings from DiComponent's modules). If the graphs vary differently further down, like with each MvpPresenter using the same Validator but with a different ProfileValidationRule versus ContactsValidationRule, you could bind ValidationRule to those different classes in your different Modules to get different behavior.
(For completeness, you would usually also have the option to use a factory like AutoFactory and pass in a parameter like the presenter to your specific container like Fragment. However, this is only really an option if you're creating your instances, and not really an option when Android forces a zero-arg public constructor so it can create Fragment instances at will.)
Looking through the names you've given to mvp-presenters, one could conclude, their complementary mvp-views should rather be separated and implemented in different fragments.
But if you wish to maintain things as-is, having only single setPresenter method declared in your fragment, probably the easiest way to deal with your problem would be to introduce separate components with complementary modules for providing desirable presenter implementations.
For this solution to work you would need to adjust your fragment to contain single declaration of setPresenter method with MVPPresenter type as an argument:
#Inject
public void setPresenter(#NonNull MVPPresenter presenter) {
this.presenter = presenter;
}
Afterwards, you'd need to provide components exposing inject(...) method and declaring usage of appropriate module. As those dependency graphs would be dependent on main component instance, they should get their own scope (tied to activity or fragment, depending on what class is actually holding the graph object).
For instance, if you were using DiComponent for providing all your dependencies with scope defined via #Singleton annotation, you'd need to declare #MyFragmentScope annotation and provide components, dependent on above-mentioned DiComponent, in order to declare injectable presenters:
import javax.inject.Scope;
#Scope
public #interface MyFragmentScope {
}
Your dependent components would look like:
#MyFragmentScope
#Component(dependencies = DiComponent.class, modules = ProfileModule.class)
public interface ProfileComponent {
void inject(MyFragment fragment);
}
with complementary module:
#Module
public class ProfileModule {
#Provides
#MyFragmentScope
MVPPresenter providesProfilePresenter() {
return new ProfilePresenter();
}
}
Note: return type is MVPPresenter, not concrete implementation.
Similarly you'd need to create ContactsComponent and ContactsModule for your ContactsPresenter.
Eventually you should use proper component instance to perform the injection. Now instead of using
diComponent.inject(myFragment)
you should use component which would provide desirable dependency.
At this point you would actually have a switch defining which presenter should be used.
In case of ProfilePresenter injecting you'd need to use:
DaggerProfileComponent.builder()
.diComponent(diComponent)
.build()
.inject(myFragment);
Or in case of ContactsPresenter injecting you'd need to use:
DaggerContactsComponent.builder()
.diComponent(diComponent)
.build()
.inject(myFragment);
It's rather common practice to use separate components for smaller parts of application like activities. It's possible to either declare such components as regular dependent ones or as sub components (see #Subcomponent documentation for reference). Starting from Dagger 2.7 there is a new way of declaring Subcomponents via #Module.subcomponents. Due to this fact there's an opportunity to decouple AppComponent from Activities Subcomponents. You may refer to sample GitHub repository from frogermcs for reference. He also has a great complementary blog post on this topic.

Difference in the use of scopes in Dagger 2

I use Dagger 2 to perform Dependency Inversion rule in my app. I was just looking on Clean Architecture example by Fernando Cejas and I had a question - what is difference between the two approaches presented below:
If I mark the class like so:
#Singleton // or #PerActivity or #PerFragment, nevermind
public class UserDataStoreFactory {
private final Context context;
private final UserCache
}
Or if I create a module, where I define a provide-method and add this module into the any component (PerActivity, PerFragment and so on, nevermind)
#Module
public class SomeModule {
#Provides
#Singleton // or #PerActivity or #PerFragment, nevermind
UserDataStoreFactory providesUserDataStoreFactory (Context context, UserCache userCache) {
return new UserDataStoreFactory(context, userCache)
}
}
The two approaches are the same: Both will allow your UserDataStoreFactory to be injected throughout your application in the Singleton (or #PerActivity/#PerFragment/nevermind) scope.
The former approach, marking the class with the scope, will only work if the class has an #Inject-annotated constructor. The second approach does not require that constructor annotation, but does also require additional boilerplate code that is subject to change when the constructor arguments change. This makes the first approach more resilient to dependency changes on UserDataStoreFactory, even though they both achieve the same end result in your graph; however, the first approach may only be possible if the class is code you can change, or that is otherwise structured for scoped dependency injection.

How to inject same Dagger 2 dependency with different implementations based on the Context?

I have a Dagger 2 dependency that is provided differently if it’s an Activity or some other context instance (e.g., a Service). Both would refer to the same interface but their implementation varies. How could I organise this using Dagger 2?
Right now, I’m trying with two different components, ActivityComponent and ContextComponent with their respective modules as follows:
#ActivityScope
#Subcomponent(
modules = {
ActivityModule.class,
ContextModule.class
})
public interface ActivityComponent {
}
#Module
public class ActivityModule {
#Provides
#MyActivityQualifier
public MyObject provideMyObject() {
}
}
#ContextScope
#Subcomponent(
modules = {
ContextModule.class
})
public interface ContextComponent { }
#Module
public class ContextModule.class {
#Provides
public MyObject provideMyObject() {
}
}
Then, if I’m using MyObject in an Activity, I have to add the qualifier as follows:
#Inject #MyActivityQualifier MyObject myObject;
This feels wrong but I’m not sure why. Is there a better way?
You are using subcomponents.
The question is: Can or should both objects be "visible" (usable, injectable, ...) at the same time?
Yes: You will have to use some sort of qualifier. Or else you can't distinguish them.
No: You can "hide" them by not exposing the dependency from the dependent component. You would have to use a normal component instead of #Subcomponent in this case, and just don't add the getMyInterface() method to your parent component.
And there aren't any other options, because you have 2 components dependent on each other. So your approach looks fine with the information you provide.
Note you can also just qualify one of your implementations. In my project, I use a qualified #Named("public") annotation for a common implementation without user data. If I don't add any qualifier I will just get the other one (unqualified).
With independent components, e.g. ActivityAComponent and ActivityBComponent you could just switch which implementation gets provided using different modules.

Categories

Resources