Should I use one Component for each Activity in Dagger 2? - android

I have the following dependencies in my Android application. What is the best approach to do this in Dagger 2?
Activity A ---- Adapter A and Adapter B and SharedPreferences
Activity B ---- Adapter B and SharedPreferences
Activity C ---- Adapter C and SharedPreferences
Will I have to make a distinct Component for each Activity? Will there have to be three separate Components?

There is some confusion about the role of Components and Modules in Dagger 2 in an Android app.
Components are for grouping similar lifecycles together.
Modules can be organized along functional lines and for testing (as per the official instructions for testing).
Some of the best examples of this are in the Google Android Architecture Blueprints Github repo. If you examine the source code there, you can see there is one single app-scoped Component (with a lifecycle of the duration of the whole app) and then separate Activity-scoped Components for the Activity and Fragment corresponding to a given functionality in a project. The Activity-scoped Components will, of course, have a lifecycle that corresponds to their respective Activity.
Let's move to your own particular use case. While one activity-scoped component that can inject all the dependencies for Activity A, B, and C may be acceptable for the simple example in the question, the situation will quickly become more complicated if requirements change and Activity A suddenly needs a new dependency with a complicated object graph. You'll then have to create a new Module for the new dependency which will only be useful for one of the three injection sites in your Component. This will, in turn, make things more difficult for testing if you are using a mock Component to test Activity B and Activity C.
Hence, I would argue that from the start it is better to maintain one Component per Activity. Activity-scoped components are cheap and easy to maintain so it is not an issue to err on the side of caution and start with one activity-scoped component per activity.
For the example you have specified, I would create an app-scoped component:
#Component( modules = { SharedPreferencesModule.class } )
#PerApp
interface AppComponent {
SharedPreferences sharedPreferences(Application app);
}
You can have your Activity components become dependent components of the app component. That way they will not have to be concerned with SharedPreferences since this is bound in the app-component and exposed to dependents:
#Component( dependencies = { AppComponent.class }, modules = { AdapterAModule.class } )
#PerActivityA
interface ActivityAComponent {
}
Note that this is the style encouraged by the dagger.android package using #ContributesAndroidInjector:
#PerActivityA
#ContributesAndroidInjector(modules = {ActivityAModule.class})
abstract YourActivity contributeYourActivityInjector();

Related

#UserScope with dagger-android

I have been forcing myself to use dagger-android for my new project, in order to reduce all the boiler plate for subComponents for activities and fragments.
But struggling with getting my #UserScope deps to be injected in those activities.
Reading up dagger-android it seems to support this kind of object graph:
Application -> Activity -> Fragment -> Sub-fragments
In my case I need a UserScope to sit between Application and Activity. i.e
|-> Activity -> Fragment -> Sub-fragments`
Application -> User - |
|-> Activity -> Fragment -> Sub-fragments`
I was wondering if there is way to achieve this WITH using ContributesAndroidInjector along with a custom subcomponent.
Any advice.
Couple of similar threads:
Make a UserScope with Dagger2 that lives for multiple activities and fragments
https://github.com/google/dagger/issues/1267
If you want to use AndroidInjection and ContributesAndroidInjector I don't think that is possible, if not in some degenerate way.
To be more precise, when you use AndroidInjection the code is getting the Application instance from your class, and then using the HasActivityInjector the App has.
Therefore the activities' sub-components must be sub-components of the Component which injected the Application class at startup.
And when the Application class is created you can't really be in a scope different than an Application/singleton scope.
Probably the question is: what is in the User scope (and not in the application scope) and where does that come from?
If you can get that when the app starts before starting any activities, then just merge the application and the user components.
If you 'create' then user scope from some data you get from, say, an activity, then you'll have the activities pass some data using intents when they launch each other.

How to create custom scope and share same instances using Dagger Android

So here are the things I know from the doc
Dagger Android under the hood is creating subcomponent for each Activity annotated with ContributesAndroidInjector
You can apply custom scope to the method where ContributesAndroidInjector is annotated to
If two sibling subcomponents have the same scope, they will still have different scope instances
If an Activity is in a subcomponent, it can have its own subcomponent which can contain Fragments. Those Fragments will share the scoped instances the Activity has.
Now my question is:
How to have one Activity be a subcomponent of another activity using Dagger Android?
I want to do this because I want to achieve things like #UserScope/#SessionScope.
From this I know that I can do it with just Dagger not Dagger Android. But with Dagger Android, you can only have the Application (which is the AndroidInjector) to inject Activity. You can not have an Activity used as a holder or host of the parent subcomponent to inject another Activity.
Am I understanding it correctly?
05/14/2018 Update:
I ended up getting rid of Dagger Android. So no more ContributesAndroidInjector, just pure Dagger. And to inject Activity/Fragment, I use the way that's recommended here. It will be something like this:
class MyActivity : AppCompatActivity() {
private val factory: ViewModelProvider.Factory = Injector.myCustomScope().factory()
}
And we are trying to make sure the factory is the only thing that Activity/Fragment needs.
So far it's been great.
How to have one Activity be a subcomponent of another activity using Dagger Android?
tl;dr You can't. Dagger Android follows a strict AppComponent > ActivityComponent > FragmentComponent scheme and there is no way to add custom scopes in-between.
I suggest you have a look at the Dagger Android source code, it's really not that much. It's basicalle a HashMap for each layer where you look up the component builder and build the subcomponent. A fragment looks at its parent Activity, an Activity looks at the Application. There is no feature where you can add custom components between layers.
What you can do is create your own variant of "Dagger Android" where you can implement your own interfaces and mix/match components as you need them. But that's quite a bit of extra work. I created a #PerScreen scope that survives configuration changes as a proof of concept if you are interested to see how you could do such a thing.
You can create a custom Scope called for example #PerScreen, also you will have #PerActvity scope. The difference between these scopes is that the #PerActivity scope will maintain shared dependencies between all activities like Context, Layout Inflater, etc. And all activity specific dependencies will be scoped as #PerScreen.
#PerApplication -> #PerActivity -> #PerScreen
This could structured like that.
I have explained scopes under the hood in my blog post, you can refer to it to get better understanding of this matter.

Dagger2 sub-component confusion in Android

My Goal :
To understand how scope works and how to Implement a UserScope that I can use over multiple Activities and reset/create a new one as required.
Methods I am using :
This Blog: http://frogermcs.github.io/building-userscope-with-dagger2/
It apparently explains the same thing that i am trying to achieve here.
Official Docs
http://frogermcs.github.io/building-userscope-with-dagger2/
Quick brief on Blog
Obviously, There is UserModule and UserComponent. Author has wrapped the creation of UserComponent under UserManager which has ApplicationScope. So UserManager is available at time of log in. when login is successful UserComponent is initialized via UserManager. Simple logic.
Now this already initialized #UserScope is used in couple of Activities, as you can see in the picture.
What I am struggling to understand
Take a look at UserComponent.
public interface UserComponent {
#Subcomponent.Builder
interface Builder {
Builder sessionModule(UserModule userModule);
UserComponent build();
}
UserDetailsActivityComponent plus(UserDetailsActivityComponent.UserDetailsActivityModule module);
RepositoriesListActivityComponent plus(RepositoriesListActivityComponent.RepositoriesListActivityModule module);
LogoutManager logoutManager();
}
Specifically UserDetailsActivityComponent and RepositoriesListActivityComponent are created through UserComponent. Like this,
#Override
protected void onUserComponentSetup(UserComponent userComponent) {
userComponent.plus(new UserDetailsActivityComponent.UserDetailsActivityModule(this)).inject(this);
}
So they first get pre-created in UserComponent through UserManager and then it calls onUserComponentSetup which then creates the appropriate Component and injects the current Activity.
I fail to comprehend with this pattern mentioned above, as I have read in the docs that we use plus(InjectionToBeDoneOn i) when we need the injection on a particular instance of InjectionToBeDoneOn. But why inject this Activity via this Component? What does this accomplish? Wouldn't it make sense to do this the conventional way in onCreate() of the activity with DaggerXYZComponent().Builder().Build().inject(activity)?
Also, I am missing decent material of how UserScope is implemented in Android which has life span from log-in to log-out but not bigger than the #Singleton scope.
we use plus(InjectionToBeDoneOn i) when we need the injection on particular instance of InjectionToBeDoneOn
Not quite. A component has basically 3 kinds of methods
SomeDependency provideDependency() which just creates / provides some dependency to subcomponents, or for manual retrieval (basically a getter)
void inject(MyAndroidFrameworkClass object) that injects an object with its dependencies
SomeSubComponent plus(SubComponentModule module) that creates a subcomponent, adding additional modules
You're mixing up 2. and 3. here.
// user scoped component
userComponent
// create a subcomponent (UserDetailsActivityComponent)
.plus(new UserDetailsActivityComponent.UserDetailsActivityModule(this))
// use the UserDetailsActivityComponent that was just created and inject with it
.inject(this);
UserDetailsActivityComponent is a subcomponent of UserComponent, which is why the userComponent gets extended .plus(somemodule) to create a subcomponent. If your submcomponent does not need additional modules you can also just use .plus() because to Dagger the important thing is the return type or signature in general.
If it returns another component, then it creates a SubComponent.
If it hast one parameter and returns void or the parameters type, then it is an inject method
If it has no parameters and returns some type is is a provides method (1.) to expose some dependency
but why inject this Activity via this Component? What does this accomplish?
If you were to create UserDetailsActivityComponent from scratch, it would only see and know about what it can provide itself. If you have some #Singleton somewhere it could not access any of it, because it is not part of the object graph.
A subcomponent extends another component, adding to the object graph. If you have a #Singleton A and your UserComponentn needs A to provide B, with a subcomponent this will work, without it you will get a cannot be provided error.
Dagger is no magic. It really just builds up a directed graph and checks whether everything is fine. It will complain if some dependencies have cyclic dependencies on one another or if some part of the graph doesn't have access to dependencies it need.
Your UserComponent holds your userdata. For simplicity lets say it holds the UserName. Now UserDetailsActivity might want to display UserName, but it needs some way to get it.
By using the #Singleton AppComponent as a parent you'd have access to some Apis, but not the user scoped ones. You could move the user scoped objects into the #Singleton AppComponent, but then you'd either have to recreate the AppComponent every time the user changes (which kind of defeats the purpose of declaring it #Singleton, or you'd have to find some other means to update / change the user.
If you want to do it the Dagger way, you create a UserComponent that adds the User to the graph. That way subcomponents can access it and do their user things.
When the user changes you have to make sure to destroy any activities / fragments that used the UserComponent, and you just recreate everything—with a new user.
wont it make sense to do in conventional way in OnCreate() of the activity with DaggerXYZComponent().Builder().Build().inject(activity)
You can do that of course. The author just put the calls to app.getAppcomponent().getUserManager().getUserComponent() or something like this into their BaseActivity and BaseUserActivity so that you wouldn't have to repeat the same lines of code every time. This method will basically still be called in onCreate, it just enables you to use the components directly, without fetching them every single time.
You can obviously remove those template methods and inline everything in onCreate, leading to duplicated code, making maintenance harder in the long run.
i am missing decent material of how UserScope is implemented in android which has life span from log-in to log-out but not bigger than #SingleTon scope.
Android doesn't help and it's your job to clean up after yourself. If the user changes you purge everything the UserComponent and its SubComponents touched, and recreate it with the new user.
You will have to store the UserComponent with the current user either in the Application class, some "real" singleton, or some "Manager" in the application component with a #Singleton scope. Every approach has their own benefits I guess.
Scopes just point out to Dagger that one object should exist within a Scope only once. It doesn't matter what you name it, or how many objects are in the Scope. Dagger only needs them to ensure that there are no dependency cycles.

DI with Dagger 2, replace sub-component on built component

I'm relatively new to Dagger2 but I've come to love the advantages of using it on my projects. I'm currently trying to understand Custom Scopes.
I have this basic app setup: ApplicationComponent, ActivityComponent, UserComponent. And this is how I intend them to work in my app
[-----------User scope-------------]
[ Activity scope ][ Activity scope ][ Activity scope ][ Activity scope ]
[-----------------------Aplication Scope (Singleton)-------------------]
In the two activities in the middle the user is logged in.
My dependency graph looks like this: AplicationComponent <- ActivityComponent <- UserComponent
UserComponent depends in ActivityComponent to work, and ActivityComponent depends on AplicationComponent.
UserComponent is just a "Specialized" ActivityComponent that also provides the current logged in user.
Activities that dont need the user will just be injected using ActivityComponent, those who need the user injected will need to use UserComponent. Hope it makes sense.
When the user first logs in, I create an UserComponent in the current activity:
ActivtyComponent activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this)) //** here, 'this' is the current Activity
.applicationComponent(MyApplication.getApp(getActivity()).getAppComponent())
.build();
UserComponent userComponent = DaggerUserComponent.builder()
.activityComponent(activityComponent)
.build();
userComponent.inject(this);
//Store user component to be retrieved by other activities
MyApplication.getApp(getActivity()).storeUserComponent(userComponent);
This works fine. Now, say that I start a new Activity and try to inject its dependencies. This time is a lot easier, I already have a UserComponent stored for this reason! I can just use that one, right?:
MyApplication.getApp(getActivity()).getUserComponent().inject(this);
Wrong!... It will crash! because that component still has the previous activity stored in its activity module (**see code above)
And I don't want to create another UserComponent, that would render the scope useless... all provides methods will be called again, am I right?
I need that specific component, not a new one. But I have to somehow swap its ActivityComponent for a new one, the new one will have this activity passed in in its activityModule... that's my question:
Is it possible? Am I looking at this the right way?
Can I change sub components in already built components?
Thanks in advance
Usually the way most tutorials show it is that you have your dependencies like AppComponent <- UserComponent <- ActivityComponent
Components create scoped objects once and if something changes you should create a new component. There is no hot swapping modules or objects in dagger 2 and if you try thinking this through you see why:
If you provide dependency A, then use A everywhere, then replace A with NEW-A and start using NEW-A from that point on...That is a really inconsistens state that you might wanna avoid.
A component should live in its respective life cycle. If your component keeps a reference to the activity it should be used along with just this activity or it will lead to a memory leak (or errors like yours).
If your user component depends on the application, then you can store that component within the application without creating any issues. Your activities then just create their own, scoped components—using and depending on either the application- or user component.

Module per Fragment / Activity in Dagger

I'm wondering if it is better to have a Module per Activity than a Module per Fragment? In one of my projects, I have an architecture to have a Module per Fragment because I use Activity just to hold and swap Fragments and nothing more. I only create Presenters and Interactors when I need them, i.e. when Fragment.onCreate() is being called.
But I can see guys are creating Module per Activity in their examples. While the idea to have an independent Module for Activity sounds perfectly reasonable as for me from a modularity perspective, but I still believe creating and keeping all objects (Presenters, Interactors) before you actually need them is not a best idea. You can't also release resources when you don't need them, which you could easily do for Module per Fragment when you just release a scoped graph in Fragment.onDestroy() event.
The examples assume you're working with several activities, each with their own clear purpose and scope. There exists a scope A for the application wide dependencies, and scopes B, C and D for each of the Activity dependencies.
You're basically adding an extra layer to the hierarchy: Application-Activity-Fragment. In that case, since you're working with a single Activity, scope A is roughly the scope for the application and activity dependencies, and B, C and D become Fragment scopes.
So yes, it makes sense to create a module for each Fragment.

Categories

Resources