feresr feresr - 4 months ago 14
Android Question

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

Answer

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.