The issue that I'm facing is that I want to have two independent scopes that don't really fall into a parent-child hierarchy. In my case, I want two types of scopes:
1) "Feature" based scopes. e.g., when a user enters a feature, a scoped component is created. When the user leaves that feature, that scope is destroyed.
2) "Activity" based scopes (this is for an Android app, sorry about the terminology if you don't use Android). When an activity is created, a scoped component is created. When the activity is destroyed, that scope is destroyed.
Subcomponents nor component dependencies work for what I'm after. This is because the feature could end before the activity is destroyed. Similarly, the activity could end before the feature is finished.
I know that I can just use provision methods instead of member injection methods and hold two separate components, but I want the simplicity of being able to just inject
all my dependencies in one go into a single object. Does anyone else have any thoughts on this?
This is the best solution I can think of. I felt the same way about coupling my Activity-related objects to other states in my app and implemented something like this.
Say you have three scopes: Application scope, Activity scope, and some Feature scope (eg like a User login state that has their profile info and such). The Activity and Feature scopes are children of the Application scopes, but Feature and Activity are unrelated siblings.
Do not directly inject the Featured scope objects directly into the Activity. Instead, inject a bridge that contains getters to the feature scoped side.
Example:
public interface UserManager {
@Nullable
User getLoggedInUser();
@Nullable
ShoppingCart getShoppingCart();
}
These methods are nullable because the user may or may not be logged in when the other objects attempt to access them. If they are null, the user is not logged in; the feature is not activated.
The implementation of this freely performs the dependency injection from the Feature scoped side since it does not reference anything from the Activity Scope.
public class UserManagerImpl implements UserManager {
@Inject
ShoppingCart cart;
@Inject
User user;
public UserManagerImpl(MyApplication application){
UserScopeComponent component = application.getUserComponent();
if(component != null) {
//only attempt injection if the component exists (user is logged in)
component.inject(this);
}
}
//put simple getters here. They will return null objects if the component didnt inject anything.
}
The ApplicationModule Provides this object. It is not an Application scope or a singleton; you want to initialize it again every time it is injected in case the login state has changed.
@Provides
//No Scope
UserManager provideUserManager(){
return new UserManagerImpl(context);
}
From then on you can inject the UserManager from anywhere in your app, call its getter, check if the output is not null, and away you go. Your Feature state is no longer coupled to the Activity state and you can be free.