I'm using Dagger for dependency injection in an Android project, and can compile and build the app fine. The object graph appears to be correct and working, but when I add dagger-compiler as a dependency to get errors at compile time, it reports some bizarre errors:
[ERROR] error: No binding for com.squareup.tape.TaskQueue<com.atami \
.mgodroid.io.NodeIndexTask> required by com.atami \
.mgodroid.ui.NodeIndexListFragment for com.atami.mgodroid \
.modules.OttoModule
[ERROR] error: No binding for com.squareup.tape.TaskQueue<com.atami \
.mgodroid.io.NodeTask> required by com.atami \
.mgodroid.ui.NodeFragment for com.atami.mgodroid.modules.OttoModule
[ERROR] error: No injectable members on com.squareup.otto.Bus. Do you want
to add an injectable constructor? required by com.atami. \
mgodroid.io.NodeIndexTaskService for
com.atami.mgodroid.modules.TaskQueueModule
The Otto error looks like the one Eric Burke mentions in his Android App Anatomy presentation about not having a @Provides
annotation, but as you can see below I do.
My Otto and TaskQueue modules are as follows:
@Module(
entryPoints = {
MGoBlogActivity.class,
NodeIndexListFragment.class,
NodeFragment.class,
NodeActivity.class,
NodeCommentFragment.class,
NodeIndexTaskService.class,
NodeTaskService.class
}
)
public class OttoModule {
@Provides
@Singleton
Bus provideBus() {
return new AsyncBus();
}
/**
* Otto EventBus that posts all events on the Android main thread
*/
private class AsyncBus extends Bus {
private final Handler mainThread = new Handler(Looper.getMainLooper());
@Override
public void post(final Object event) {
mainThread.post(new Runnable() {
@Override
public void run() {
AsyncBus.super.post(event);
}
});
}
}
}
...
@Module(
entryPoints = {
NodeIndexListFragment.class,
NodeFragment.class,
NodeIndexTaskService.class,
NodeTaskService.class
}
)
public class TaskQueueModule {
private final Context appContext;
public TaskQueueModule(Context appContext) {
this.appContext = appContext;
}
public static class IOTaskInjector<T extends Task>
implements TaskInjector<T> {
Context context;
/**
* Injects Dagger dependencies into Tasks added to TaskQueues
*
* @param context the application Context
*/
public IOTaskInjector(Context context) {
this.context = context;
}
@Override
public void injectMembers(T task) {
((MGoBlogApplication) context.getApplicationContext())
.objectGraph().inject(task);
}
}
public static class ServiceStarter<T extends Task>
implements ObjectQueue.Listener<T> {
Context context;
Class<? extends Service> service;
/**
* Starts the provided service when a Task is added to the queue
*
* @param context the application Context
* @param service the Service to start
*/
public ServiceStarter(Context context,
Class<? extends Service> service) {
this.context = context;
this.service = service;
}
@Override
public void onAdd(ObjectQueue<T> queue, T entry) {
context.startService(new Intent(context, service));
}
@Override
public void onRemove(ObjectQueue<T> queue) {
}
}
@Provides
@Singleton
TaskQueue<NodeIndexTask> provideNodeIndexTaskQueue() {
ObjectQueue<NodeIndexTask> delegate =
new InMemoryObjectQueue<NodeIndexTask>();
TaskQueue<NodeIndexTask> queue = new TaskQueue<NodeIndexTask>(
delegate, new IOTaskInjector<NodeIndexTask>(appContext));
queue.setListener(new ServiceStarter<NodeIndexTask>(
appContext, NodeIndexTaskService.class));
return queue;
}
@Provides
@Singleton
TaskQueue<NodeTask> provideNodeTaskQueue() {
ObjectQueue<NodeTask> delegate =
new InMemoryObjectQueue<NodeTask>();
TaskQueue<NodeTask> queue = new TaskQueue<NodeTask>(
delegate, new IOTaskInjector<NodeTask>(appContext));
queue.setListener(new ServiceStarter<NodeTask>(
appContext, NodeTaskService.class));
return queue;
}
}
...
/**
* Module that includes all of the app's modules. Used by Dagger
* for compile time validation of injections and modules.
*/
@Module(
includes = {
MGoBlogAPIModule.class,
OttoModule.class,
TaskQueueModule.class
}
)
public class MGoBlogAppModule {
}
Defining dependencies (object consumers) You use the @Inject annotation to define a dependency. If you annotate a constructor with @Inject , Dagger 2 can also use an instance of this object to fulfill dependencies. This was done to avoid the definition of lots of @Provides methods for these objects.
onCreate() , an activity attaches fragments that might want to access activity bindings. When using fragments, inject Dagger in the fragment's onAttach() method. In this case, it can be done before or after calling super. onAttach() .
Inject values at runtime with UI in Dagger2:pureMathModule("Book Name") . build() . inject(this); The difference between DaggerComponent create() and in build() is - create() works when no runtime argument is passed into the constructor, else we use build() method.
Dagger needs to use an annotation processor to scan our code and generate appropriate classes. It may take some time and it may slow down our build. On the other hand, because Koin resolves dependencies at runtime it has slightly worse runtime performance.
Dagger's full graph analysis works from a complete module. i.e. @Module(complete = true), which is the default. Because it's the default, dagger will, by default, assume that all bindings are available from that module or those modules it includes explicitly.
In this case, you've given two modules that you claim are complete, but Dagger has no way to tie these together at compile time without an additional signal. In short, without OttoModule knowing about TaskQueueModule, the compiler will attempt to analyse OttoModule for its claimed completeness, and fail, because it doesn't now about TaskQueueModule.
Modify OttoModule's annotation as such:
@Module(
includes = TaskQueueModule.class,
entryPoints = {
MGoBlogActivity.class,
NodeFragment.class,
NodeActivity.class,
NodeCommentFragment.class,
NodeIndexTaskService.class,
NodeTaskService.class
}
)
and then Dagger will know that for OttoModule to be complete, it includes the other module as part of its full definition.
Note:dagger-compiler can't detect that TaskQueueModule is there on the class path and just "know" that the developer intended it to be used with OttoModule without that additional signal. For instance, you might have several modules which define task queues and which one would it select? The declaration must be explicit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With