I am trying to migrate the company app to dagger 2.10 and the AndroidInjector.inject
method, but I think I found a problem. The app uses custom scopes… like the feature Login has 3 activities (each one with it`s own dagger module) and one LoginModule that is responsible for sharing singletons that only should live in this scope. So in the first Activity I used to execute something like:
public class LoginActivity extends AppCompatActivity{
public void onCreate(Bundle bla){
LoginActivityComponent activityComponent = ((CustomApplication) getApplicationContext())
.plus(new LoginModule()) // generates LoginComponent and save the reference in the CustomApplication
.plus(new LoginActivityModule(this));
activityComponent.inject(this);
...
}
In the others activities I just execute ((CustomApplication) getApplicationContext()).getLoginComponent().plus(new ForgetPasswordModule()).inject(this)
How can I archive the same behavior when using AndroidInjector ?
The single-subcomponent cheat
Rather than the normal implementation in your Application:
Just move the activity-binding modules to your LoginComponent and delegate to the
DispatchingAndroidInjector<Activity>
from your LoginComponent instead:This is the least amount of ongoing maintenance, but it seems pretty backwards, because you're creating your LoginComponent up front. However, if LoginComponent is cheap and is your only subcomponent of this style, then everything works perfectly: LoginComponent's injector can see the multibindings in its parents, so LoginComponent's ActivityInjector will always work even for bindings in the parent.
Because the bindings of non-login activities still reside in the ApplicationComponent, those activities won't be able to use bindings from the parent component. Otherwise, though, this is tantamount to merging your LoginComponent into your ApplicationComponent, which probably isn't an option or else you'd've done it that way.
Delegating AndroidInjector
If your LoginComponent is expensive to create, then as an alternative you could move the
getOrCreateLoginComponent()
call behind aninstanceof
check:This means that you would need to keep a separate list (maybe as a field in LoginComponent or LoginModule) of activities that LoginComponent can handle, but if you want to avoid instantiating LoginComponent until you're sure that you're injecting a login-related activity, here's how you'd check it. The above approach also scales well to multiple subcomponents, because you're always calling
inject
on exactly oneDispatchingAndroidInjector<Activity>
from exactly one Component.Hybrid alternative
Because the Map presence check is likely pretty fast, you could also avoid that extra list by checking with the main injector first before throwing it to the login component. Of course, that starts to get ugly if you've got multiple subcomponents of that style.
Hopefully, between the three, you won't find Android injection here to be "worse than pain from any disease or wound known in the universe".