I'm quite new to Dagger 2 and I'm looking for a way to have a "configurable component".
Essentially this is what I want to achieve:
public interface ErrorReporter{
...
}
public class ConsoleErrorReporter implements ErrorReporter{
... // Print to System.err
}
public class DialogErrorReporter implements ErrorReporter{
... // Show modal dialog to user
}
#Module
public interface UIModule{
#Provides
ErrorReporter provideErrorReporter();
}
#Module
public class ConsoleUIModule{
#Override
#Provides
ErrorReporter provideErrorReporter(ConsoleErrorReporter cer){
return cer;
}
}
#Module
public class GraphicalUIModule{
#Override
#Provides
ErrorReporter provideErrorReporter(DialogErrorReporter der){
return der;
}
}
#Component(modules = {UIModule.class, OtherUniversalModule.class})
public interface ApplicationComponent{
ErrorReporter errorReporter();
}
void main(String[] args){
final UIModule uiModule;
if(args.length == 1 && args[0].equals("gui")){
uiModule = new GraphicalUIModule();
}else{
uiModule = new ConsoleUIModule();
}
DaggerApplicationComponentdac = DaggerApplicationComponent.builder()
.uiModule(uiModule).build();
dac.errorReporter().showError("Hello world!");
}
The above fails with #Provides methods cannot be abstract unfortunately both for interfaces and abstract classes. I have also tried non-abstract base class with concrete implementations that return null and then overriding these in sub classes. However this also fails with #Provides methods may not override another method.
In short I want to define a contract for a module and choose different modules during runtime. I know that Dagger 2 compile time validates the object graph, but if I have a well defined contract that should still be possible right? Or am I forced to create two different components with duplicate code for both user interfaces? Are there other solutions that I'm missing?
I don't think using a module this way is possible, because...
Suppose you have the following two constructors for your classes
#Inject ConsoleErrorReporter(Console console);
#Inject DialogErrorReporter(Graphics graphics);
This would mean that ConsoleUIModule would require a Console and DialogErrorReporter would require a Graphics object to create their respecitve implementation of ErrorReporter.
But if dagger only knows about UIModule because you use the interface there...well...it could not provide the dependencies for either, because it doesn't know about any of them.
And if you don't know the dependencies building a dependency graph at compile time won't work. Also this won't compile even without dagger because provideErrorReporter(ConsoleErrorReporter cer) does not override provideErrorReporter().
What you can and should do is use different components. Because a component is the thing that actually knows how to provide things. And a component already is an interface—and that's what you wanted, right?
You can have component dependencies, where one component depends on another. E.g. have a DependentComponent that provides a NeedsErrorReporter that needs an implementation of ErrorReporter. We also depend on an interface, rather than the actual component (and that's what you wanted after all, right?)
You then implement the interface by actual components, and each component has its respective modules (and maybe even further dependencies). In the end you have a component that you can switch and will provide different versions of an object, properly encapsulated!
#Component(dependencies = UIComponent.class) /* <- an interface! */
interface DependentComponent {
NeedsErrorReporter needsErrorReporter();
}
class NeedsErrorReporter {
#Inject public NeedsErrorReporter(ErrorReporter reporter) { }
}
/* this is _not_ a component, but a simple interface! */
interface UIComponent {
ErrorReporter errorReporter();
}
/* Console */
#Component(modules = ConsoleUIModule.class)
interface ConsoleUIComponent extends UIComponent { }
#Module interface ConsoleUIModule {
#Binds ErrorReporter provideErrorReporter(ConsoleErrorReporter cer);
}
/* Graphic */
#Component(modules = GraphicalUIModule.class)
interface GraphicUIComponent extends UIComponent { }
#Module interface GraphicalUIModule {
#Binds ErrorReporter provideErrorReporter(DialogErrorReporter der);
}
/* The error reporter variants */
interface ErrorReporter {
}
class ConsoleErrorReporter implements ErrorReporter {
#Inject public ConsoleErrorReporter() { }
}
class DialogErrorReporter implements ErrorReporter {
#Inject public DialogErrorReporter() { }
}
Now all you have to do is pick the right component ;)
DaggerDependentComponent.builder().uIComponent(DaggerConsoleUIComponent.create()).build();
// or
DaggerDependentComponent.builder().uIComponent(DaggerGraphicUIComponent.create()).build();
Related
Decided to give Dagger 2 a try while reorganizing a certain project, however there are still some things not entirely clear to me. What would be the best approach to supporting a chain of dependencies that require a runtime parameter at it's root?
A quick simplified example: let's say there's a base class called "SpecificDatabaseService", which requires a runtime parameter like "login". Then there's a "SampleDao" class, that requires a DatabaseService [amongst some other dependencies]. Finally 2 classes named "FirstProcessor" and "SecondProcessor" would both require a SampleDao, like so:
public class SpecificDatabaseService implements DatabaseService {
public SpecificDatabaseService(String login) { // setup }
}
public class SampleDao {
public SampleDao(DatabaseService service) { // setup }
}
public class FirstProcessor {
public FirstProcessor(SampleDao dao) { // setup }
}
public class SecondProcessor {
public SecondProcessor(SampleDao dao) { // setup }
}
Next thing I'm creating SpecificDatabaseServiceModule and a DatabaseServiceComponent in order to do some additional stuff to DatabaseService later.
#Module
interface SpecificDatabaseServiceModule {
#Binds
DatabaseService bindDatabaseService(SpecificDatabaseService databaseService);
}
#Singleton
#Component(modules = { SpecificDatabaseServiceModule.class })
interface DatabaseServiceComponent {
DatabaseService getDatabaseService();
#Component.Factory
interface Factory {
DatabaseServiceComponent create(#BindsInstance #Named("login") String login);
}
}
Now when I need to create a FirstProcessor, someone has to provide a "login" for FirstProcessorComponent once again (same as with SecondProcessor). And if there's a class, which uses a FirstProcessor as a dependency, then again it needs a "login" to be provided. As the chain grows, it becomes more and more difficult to use Dagger.
Is there an easier way or approach of passing the "login" value once and just sharing module/component. For instance FirstProcessorComponent depends on DatabaseServiceComponent and if Dagger know how to construct DatabaseService, it's able to construct FirstProcessor as well. Hasn't been unable to get it working up till now.
I have an app with activities and fragments with dependencies injected via dagger 2
I am able to do field injection in activities and fragments but not able to do constructor injection in other classes.
Here's my relevant code
#Module
public abstract class MainFragmentProvider {
#ContributesAndroidInjector(modules = HomeFragmentModule.class)
abstract HomeFragment provideHomeFragmentFactory();
}
and
#Module
public class HomeFragmentModule {
...
#Provides
static HomePresenter provideHomePresenter(HomeView homeView, HomeInteractor homeInteractor) {
return new HomePresenter(homeView, homeInteractor);
}
How can I write code so that I can get dependencies directly in HomePresenter by constructor injection instead of writing provideMethods in module. I am doing this because every time I want to change the constructor arguments in this case, I need to change the module code as well.
How can I do something like this in HomePresenter's constructor?
#Inject
public HomePresenter(HomeView homeView, HomeInteractor homeInteractor) {
this.homeInteractor = homeInteractor;
this.homeView = homeView;
}
To inject constructor, Dagger has to know where to get parameters passed to it i.e.
you have to provide
HomeView homeView, HomeInteractor homeInteractor
So create also methods for providing other dependencies:
#Provides
static HomeView provideHomeView() {
return ...
}
#Provides
static HomeInteractor provideHomeInteractor() {
return ...
}
I don't know much about the android extensions for dagger 2 but as far as I know there are two ways to achieve the result you are looking for.
In the relevant component you can specify a method with your type:
interface SomeComponent {
HomePresenter presenter(); // Method name does not matter here, only the type
}
and access it like this
class Home {
HomePresenter presenter;
void initialize() { //This could be your onCreate or wherever you typically inject
presenter = getSomeComponent().presenter();
}
}
or you can request it if you specify an inject method for your Home object:
interface SomeComponent {
void inject(Home home);
}
class Home {
#Inject HomePresenter presenter;
void initialize(){
getSomeComponent().inject(this);
}
}
In both cases you must ensure your Component includes the appropriate Modules.
The problem I am facing is that I have a base class and multiple child class. For resolving the particular child class I am using #Named annotation in Dagger 2. What I am trying to achieve is if I Inject with #Named("Child3") and there is not Provide with #Named("Child3") then I should get instance of Base class by default.
public class BaseClass {
public void haveFun(){
System.out.print("Having fun base");
}
}
public class Child1 extends BaseClass {
#Override
public void haveFun() {
System.out.print("Having fun Child1");
}
}
public class Child2 extends BaseClass {
#Override
public void haveFun() {
System.out.print("Having fun Child2");
}
}
Now in the module I am providing the objects like this:
#Provides
#Named("Child1")
static BaseClass provideChild1(){
return new Child1();
}
#Provides
#Named("Child2")
static BaseClass provideChild2(){
return new Child2();
}
#Provides
static BaseClass provideBaseClass(){
return new BaseClass();
}
Now in my activity I am injecting like this:
public class ReceiptActivity extends AppCompatActivity {
#Inject #Named("Child1") BaseClass child1;
#Inject #Named("Child2") BaseClass child2;
#Inject #Named("Child3") BaseClass child3;
// ...
}
As #Named("Child3") is not provided there is a compile time error, but what I want is if #Named("Child3") is not there I should get a BaseClass instance.
How can I achieve this?
Unfortunately, qualified bindings (bindings using qualifier annotations like #Named) don't really have a fallback or default. Each binding is different, and the different bindings aren't considered to be related. This is also the same for bindings lacking any sort of qualifier: #Named("Child3") BaseClass and BaseClass are completely different bindings to Dagger.
This makes sense, too: #Named("Porsche") Engine and #Named("Lawnmower") Engine are never really going to substitute for one another, despite sharing a base type. They're entirely different dependencies, and if you're missing a #Named("Porsche") Engine, Dagger follows the policy that it should fail at compile time rather than scrounging around for a mismatched or unqualified Engine.
If this is known at compile time, you can bind your own default:
#Binds #Named("Child3") BaseClass bindChild3(BaseClass baseClass);
// or the reverse, if BaseClass weren't bound and you wanted it to default
// to #Named("Child1") BaseClass
#Binds BaseClass bindBaseClass(#Named("Child1") BaseClass child1);
You could also bind a Map or use Multibindings to indicate the substitutability or flexibility you're looking for. Rather than injecting the binding itself, you'd inject a Map, or inject a Factory that encapsulates the map and pulls out the right binding for you.
// This uses Multibindings, but you could manually create a Map instead.
#Binds #IntoMap #StringKey("Child1")
abstract BaseClass provideChild1(Child1 child1);
#Binds #IntoMap #StringKey("Child2")
abstract BaseClass provideChild2(Child2 child2);
// Then in your consumer...
#Inject Map<String, BaseClass> mapOfBaseClasses;
#Inject BaseClass baseClass;
// Or make an injectable Factory:
public class YourClassFactory {
private final Map<String, Provider<BaseClass>> baseClassMap;
private final Provider<BaseClass> baseClassProvider;
#Inject public YourClassFactory(/* ... */) { /* set fields */ }
public BaseClass get(String key) { /* write fallback logic here */ }
}
If you have a specific binding that may be present or absent, you can also use #BindsOptionalOf to indicate that the binding is allowed to be missing at compile time, and then you can detect it at runtime.
#BindsOptionalOf #Named("Child3")
abstract BaseClass provideOptionalOfChild3();
// Then in your consumer:
private final BaseClass baseClass;
#Inject public YourConsumer(
#Named("Child3") Optional<BaseClass> optionalChild3,
Provider<BaseClass> defaultBaseClass) {
baseClass =
optionalChild3.isPresent()
? optionalChild3.get()
: defaultBaseClass.get();
}
I have the following Dagger2 Architecture in my app:
-- AppComponent (#PerApplication)
-- UserComponent (#PerUser)
-- ActivityComponent (#PerActivity)
-- ChatComponent (#PerActivity) <-- 1
Where:
AppComponent:
#PerApplication
#Component(modules = {ApplicationModule.class, StorageModule.class, NetworkModule.class})
public interface ApplicationComponent {
UserComponent plus(UserModule userComponent);
//Exposed to sub-graphs.
Context application();
}
UserComponent:
#PerUser
#Subcomponent(modules = {UserModule.class, RosterModule.class})
public interface UserComponent {
ActivityComponent plus(ActivityModule activityModule);
User getMe();
UserRepository userRepository();
}
ActivityComponent:
#PerActivity
#Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
ChatComponent plus(ChatModule chatComponent);
//Exposed to sub-graphs.
Context context();
}
ChatComponent:
#PerActivity
#Subcomponent(modules = {ChatModule.class})
public interface ChatComponent {
void inject(ChatListFragment chatListFragment);
void inject(ConversationFragment conversationFragment);
void inject(NewConversationFragment newConversationFragment);
void inject(CloudFilesFragment cloudFilesFragment);
void inject(ChatActivity chatActivity);
void inject(ConversationActivity conversationActivity);
void inject(NewConversationActivity newConversationActivity);
void inject(NewGroupActivity newGroupActivity);
void inject(NewGroupFragment newGroupFragment);
}
I'm facing 2 problems:
First, how can I inject different Context to my classes ? Either App or Activity ??
And secondly, I'm facing a bizarre issue when trying to compile my code, the error is:
Error:(23, 10) error:
br.com.animaeducacao.ulife.domain.interactor.UseCase cannot be
provided without an #Provides-annotated method.
br.com.animaeducacao.ulife.presentation.view.fragment.ChatListFragment.chatListPresenter
[injected field of type:
br.com.animaeducacao.ulife.presentation.presenter.ChatListPresenter
chatListPresenter]
br.com.animaeducacao.ulife.presentation.presenter.ChatListPresenter.(br.com.animaeducacao.ulife.domain.interactor.UseCase
chatDialogsUseCase,
br.com.animaeducacao.ulife.domain.interactor.UseCase
adviceUserPresence, android.content.Context context) [parameter:
#javax.inject.Named("getChatDialogs")
br.com.animaeducacao.ulife.domain.interactor.UseCase
chatDialogsUseCase]
My ChatListFragment is:
#PerActivity
public class ChatListFragment extends BaseFragment implements ChatListView {
#Inject
ChatListPresenter chatListPresenter;
...
//called onActivityCreated()
private void initialize() {
this.getComponent(ChatComponent.class).inject(this);
}
BaseFragment:
protected <C> C getComponent(Class<C> componentType) {
return componentType.cast(((HasComponent<C>)getActivity()).getComponent());
}
ChatListPresenter:
#PerActivity
public class ChatListPresenter implements Presenter {
private final UseCase chatDialogsUseCase;
private final UseCase adviceUserPresence;
private final Context context;
private ChatListView chatListView;
#Inject
public ChatListPresenter(#Named("getChatDialogs") UseCase chatDialogsUseCase,
#Named("adviceUserPresence") UseCase adviceUserPresence,
Context context) {
this.chatDialogsUseCase = chatDialogsUseCase;
this.adviceUserPresence = adviceUserPresence;
this.context = context;
}
The problem is, in my ChatModule class I have implemented all the #Provides necessary:
#Provides
#PerActivity
#Named("getChatDialogs")
public UseCase provideChatDialogs(#Named("transactionalChatRepository") ChatRepository chatRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
return new GetUserChatDialogs(chatRepository, threadExecutor, postExecutionThread);
}
Is this a good approach ? Why is this not compiling, what I am missing here ?
Sorry for the long post and thanks !
Ah, you have multiple problems.
1.) While you're using the subscoping correctly to a point (you are making #Subcomponents properly at first), the ChatComponent doesn't actually subscope its parent component - basically, ChatComponent cannot be #PerActivity, it needs to be a fourth scope.
#Subcomponent annotation is just a way to create a subscoped component without having to specify it as a component dependency. It still needs its own "more specific" scope.
2.) to make subscoping work, you need to specify provision methods in your component for every dependency that that component is meant to provide, so that the subscoped components can inherit them.
For example, your ApplicationComponent doesn't have provision methods for what is in StorageModule, and therefore the dependencies provided by StorageModule cannot be inherited to subscoped components.
I however am not sure if you can just specify the class you're providing if it is not inside a module, and instead it is annotated with #Inject constructor and the class is marked with the scope.
Also, to allow in a scope hierarchy A->B->C for C to inherit from A, then B needs to have the provision methods of A as well.
So UserComponent extends ApplicationComponent is necessary, and ActivityComponent extends UserComponent, and ChatComponent extends ActivityComponent.
3.) You should use the #Named("application") and #Named("activity") annotations to specify two different Context, or instead just refer to them as Application and Activity in your module so that they don't get mixed up.
Consider a MVP-ish set of types. An abstract Presenter exists, with a View interface:
public interface View {
//...
}
public abstract class AbstractPresenter<V extends View> {
#Inject V view;
//...
}
Then, lets have a specific concrete presenter subclass, with its view interface and implementation:
public interface LoginView extends View {
//...
}
public LoginPresenter extends AbstractPresenter<LoginView> {
//...
}
public class LoginViewImpl implements LoginView {
//...
}
In a Dagger module, of course we would define a #Provides method:
#Provides
LoginView provideLoginView() {
return new LoginViewImpl();
}
In Guice you could write this the same way, or just bind(LoginView.class).to(LoginViewImpl.class).
However, in Dagger (both v1 and the 2.0-SNAPSHOT from Google), this produces an error, since it can't figure out what V is when creating the binding wiring for AbstractPresenter<V>. On the other hand, Guice figures out that that because it is actually creating a LoginPresenter, so it needs an implementation of LoginView.
Dagger 1.2.2:
foo.bar.AbstractPresenter$$InjectAdapter.java:[21,31] cannot find symbol
symbol: class V
location: class foo.bar.AbstractPresenter$$InjectAdapter
Dagger 2.0-SNAPSHOT:
Caused by: java.lang.IllegalArgumentException: V
at dagger.internal.codegen.writer.TypeNames$2.defaultAction(TypeNames.java:39)
at dagger.internal.codegen.writer.TypeNames$2.defaultAction(TypeNames.java:36)
at javax.lang.model.util.SimpleTypeVisitor6.visitTypeVariable(SimpleTypeVisitor6.java:179)
at com.sun.tools.javac.code.Type$TypeVar.accept(Type.java:1052)
at dagger.internal.codegen.writer.TypeNames.forTypeMirror(TypeNames.java:36)
at dagger.internal.codegen.MembersInjectorGenerator.write(MembersInjectorGenerator.java:142)
at dagger.internal.codegen.MembersInjectorGenerator.write(MembersInjectorGenerator.java:61)
at dagger.internal.codegen.SourceFileGenerator.generate(SourceFileGenerator.java:53)
at dagger.internal.codegen.InjectBindingRegistry.generateSourcesForRequiredBindings(InjectBindingRegistry.java:101)
at dagger.internal.codegen.ComponentProcessor.process(ComponentProcessor.java:149)
My question: Is this a bug? Is this a missing feature? Or is this a performance issue that Dagger is protecting us from (a la SerializableTypeOracleBuilder in GWT RPC)?
Note that this same issue occurs when V is referred to as Provider<V>, Lazy<V>, etc.
That looks like a bug as it shouldn't throw an exception, but it should log a warning explaining that type parameters need to be bound to a specific type.
The rest is for Dagger2, and I'm using 2.1-SNAPSHOT. You haven't provided an example #Component that will do the injection and without it Dagger2 2.1-SNAPSHOT doesn't actually report a problem. It's possible that it has already fixed your problem and I'm seeing a slightly different version but if not then I presume your component looks something like this:
#Component
public interface PresenterComponent {
<V extends View> void inject(AbstractPresenter<V> presenter);
}
When Dagger2 is processing this it cannot determine a concrete type for V, and so it doesn't know what type to insert. It can't just insert say LoginView because that would break if it was passed a AbstractPresenter<LogoutView>.
However, if you use say the following then Dagger2 can determine that it needs to inject a LoginView into AbstractPresenter<LoginView> and will do so safely.
#Module
public class LoginModule {
#Provides LoginView provideLoginView() {
return new LoginViewImpl();
}
}
#Component(modules = LoginModule.class)
public interface LoginComponent {
void inject(LoginPresenter presenter);
}
Unless you have no control over when an object is created, e.g. if some framework creates it for you and then passes in for you to initialize, it is much better to use #Inject on the constructor if you can, e.g. like this:
public LoginPresenter extends AbstractPresenter<LoginView> {
//...
#Inject LoginPresenter(LoginView view) {
super(view);
//...
}
}
This is because of the Type arguments. Injects does not work when u have a type arguments. U need to do something like this,
bind(new LoginPresenter<LoginViewImpl>(){});