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.
Related
Scenario 1 - If we use ViewModels to communicate between fragments, then the ViewModel has to be created by activity reference and hence going to stay there in memory until the activity is destroyed.
Scenario 2 - In master-detail flow ViewModel makes our life easier but again the memory usage issue is there.
Scenario 3 - We have viewModelScope in the new version of arch library to cancel jobs with Fragment/Activity lifecycles, but if ViewModel is created with activity reference then it's going to stay there until activity is destroyed. Hence the job can still be executing and fragment is already gone.
You can use ViewModels to communication between two different fragments(aka SharedViewmodels) because it's simple but it's not perfect.
As you know the SharedViewModels must be alive until the first joint parent (activity or fragment) is alive.
but wait ...
What is the purpose of ViewModels?
Are we create ViewModels just for communication between fragments? Absolutely not.
Is the use of ViewModels against the purpose of ViewModels? no, I say it's not perfect use them for communication between fragments but if you have a small project you can use them because it's simple.
So what can I do instead of using ViewModels for communication between fragments? You can build independent UI components and use them for communication between fragments.
What is the advantage of building this weird independent UI components? In this way, each component has its own ViewModel (or Presenter) and they haven't any parent/child relation; Instead, they updated from the same business logic or in reactive programming they are just observing the same Model.
What is the meaning of building independent UI components and how to build them? if you are already familiar with reactive programming, I recommend reading this amazing blog post by Hannes Dorfmann; If you don't, I simply propose using EventBus library for communication between fragments but You will soon realize that too much usage of this library leads to spaghetti code.
Scenarios
If the fragments are not part of a flow/group, then don't share the ViewModel, just pass some id/data to the new fragment, create its own viewmodel, and query the data for the fragment from its own viewmodel.
If the fragments are part of some flow/group (cart/checkout/booking flow, multi-screen registration process, viewpager fragments, etc) and the logic is common enough, then share the viewmodels between the fragments. In single-activity architecture, I put these flow/process in its own root parent fragment that acts as a host and is used to create the scope of the viewmodel. For example:
MainActivity ->
-> RootAuthFragment
-> SplashFragment (replace with below)
-> LoginFragment (add to backstack with below or onsuccess login go to MainFragment)
-> SignupFragment (onsuccess go to Main)
-> MainFragment (replace with RootAuthFragment)
In the above scenario, you can share the viewmodel between login and signup screens with RootAuthFragment's scope. If you have a multi-screen signup process, then you could move that into separate root fragment and create a separate viewmodel for the signup flow.
Bundle vs ViewModels:
Bundles are used to pass some values. So, use it just for that. I use bundles to usually pass primitive data types or enums and based on that I query the actual data from the viewmodel (through android room or retrofit) or if the data objects are small enough, I make them parcelable and just pass that.
If you have a shared ViewModel and it's becoming a god class and does a lot of different things, then it means those fragments need separate ViewModels. Don't share the ViewModel just for data. Share the ViewModel for the common/shared behaviour/data/logic (whichever makes sense for your particular use cases)
I prefer you should use View Models approach if you are using single activity architecture. To justify my answer I will clear your scenarios here.
Scenario 1 - If we use ViewModels to communicate between fragments, then the ViewModel has to be created by activity reference and hence going to stay there in memory until the activity is destroyed.
Scenario 2 - In master-detail flow ViewModel makes our life easier but again the memory usage issue is there.
As for memory you are already holding information into memory there is no escaping there. If you don't need data for stay there then you can clear data from models also but again it will kill the purpose of storing data in the first place.
If you pass data using bundle it's also going to take memory there also.
Scenario 3 - We have viewModelScope in the new version of arch library to cancel jobs with Fragment/Activity lifecycles, but if ViewModel is created with activity reference then it's going to stay there until activity is destroyed. Hence the job can still be executing and fragment is already gone.
That's the main purpose of using view models it will store the last state for user where he left.
As per https://developer.android.com/topic/libraries/architecture/viewmodel states
This approach offers the following benefits:
The activity does not need to do anything, or know anything about this communication.
Fragments don't need to know about each other besides the SharedViewModel contract. If one of the fragments disappears, the other one keeps working as usual.
Each fragment has its own lifecycle, and is not affected by the lifecycle of the other one. If one fragment replaces the other one, the UI continues to work without any problems.
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.
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();
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.
I've read a lot of posts and tutorials about dagger 2:
http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/
https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2
http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/
https://github.com/konmik/konmik.github.io/wiki/Snorkeling-with-Dagger-2
What determines the lifecycle of a component (object graph) in Dagger 2?
etc.
But I am still confused about the lifecycle of a component, and how it relates to module and scope. I want to make sure I don't create multiple instances of an object when I only want a Singleton. Hope someone can shed some light on these:
What's the lifecycle of a component that's built in the application class?
What's the lifecycle of a component that's built in the Activity or Fragment class?
If I want a singleton instance from a component, do I must annotate the component with #Singleton or a custom made scope and build that component in the application class?
If I build a component in the application class, does that mean all the object instances available through this component will be a singleton instance throughout the app until the app is killed or restarted?
I have a component with a custom scope let's say #ActivityScope, and I build that component in an Activity, will the object instances injected through this component be destroyed automatically after this activity's onDestroy() is called?
Again I have a component with a custom scope let's say #ActivityScope, and I build this component in ActivityA and ActivityB, will ActivityA and ActivityB share the same object instances from this component or they will have their own instances of the same object?
How I understand it:
And keep in mind two things (when I first read 1) it it made everything cleaner to me):
1)Components live as long as you want it to or as long as class that created component wasn't destroyed (like android activity or fragment)
2)If you don't annotate you provide methods with annotation (must be the same as component annotation) new objects will be created every time you request for them
What's the lifecycle of a component that's built in the application class?
Component built in application class lives as long as you want. I mean you can create it at any time and remove it at any time as long as you create it in class that extends android Application class (this way component object will live as long as your Android App is running) in contrast to component that's built in activity class - it will live as long as activity is alive so it may be destroyed for example on orientation change.
Keep in mind that if for some reason you didn't create your ApplicationComponent in onCreate() method of Application class (for example you created it later when something happened) it can be destroyed (nulled) when Android OS is low on memory and user closed your app, and then when user comes back to your app (to last visible activity) when it has been killed earlier and you ask your app component to do something then check if it's not null
What's the lifecycle of a component that's built in the Activity or
Fragment class?
I partially answered it in above answer. If you create your component inside Fragment/Activity it lives as long as you want or as long as activity or fragment is not destroyed due to orientation change or low memory
If I want a singleton instance from a component, do I must annotate
the component with #Singleton or a custom made scope and build that
component in the application class?
It depends where you want to use this singleton. If you want singleton in single activity you may create for example #ActivityScope annotation and annotate provide methods and ActivityComponent with this annotation, then you create your ActivityComponent inside onCreate() Activity method and you have a singleton as long as your activity lives (it may be helpfull if you plan to have a singleton shared between different fragments from same activity).
If you want singleton between different acctivities/fragment in app the best way to do that would be to create it in AppModule and annotate provide method and app component with singleton annotation.
If I build a component in the application class, does that mean all
the object instances available through this component will be a
singleton instance throughout the app until the app is killed or
restarted?
If you annotate provide methods with #Singleton annotation then yes
I have a component with a custom scope let's say #ActivityScope, and I
build that component in an Activity, will the object instances
injected through this component be destroyed automatically after this
activity's onDestroy() is called?
Yes
Again I have a component with a custom scope let's say #ActivityScope,
and I build this component in ActivityA and ActivityB, will ActivityA
and ActivityB share the same object instances from this component or
they will have their own instances of the same object?
They will have their own instances