I've got a simple Dagger 2 test-setup, based on http://konmik.github.io/snorkeling-with-dagger-2.html. It injects a PreferenceLogger which outputs all the preferences. In the injected class, I can @Inject more classes.
public class MainActivity extends Activity {
@Inject PreferencesLogger logger;
@Inject MainPresenter presenter;
@Override protected void onCreate(Bundle savedInstanceState) {
MyApplication.getComponent().inject(this);
presenter.doStuff();
logger.log(this);
}
}
public class PreferencesLogger {
@Inject OkHttpClient client;
@Inject public PreferencesLogger() {}
public void log(Contect context) {
// this.client is available
}
}
When I run this, logger is set, and inside PreferencesLogger.log the OkHttpClient is correctly set. So this example works as expected. Now I'm trying to get a MVP structure in place. There's a MainPresenter interface with an implementation. In the MainActivity I set an:
@Inject MainPresenter presenter;
so I could switch this MainPresenter with an alternative (debug or test) implementation. Ofcourse, now I need a Module to specify what implementation I want to use.
public interface MainPresenter {
void doStuff();
}
public class MainPresenterImpl implements MainPresenter {
@Inject OkHttpClient client;
public MainPresenterImpl() {}
@Override public void doStuff() {
// this.client is not available
}
}
@Module public class MainActivityModule {
@Provides MainPresenter provideMainPresenter() {
return new MainPresenterImpl();
}
}
A problem now occurs that the OkHttpClient isn't injected anymore. Ofcourse I could alter the Module to accept a parameter OkHttpClient, but I don't think this is the suggested way to do it. Is there a reason why the MainPresenterImpl doesn't Inject correctly?
We can simply use @Inject at constructor! TL DR; If you have provide methods which just call constructor of implementation classes to inject interfaces, use @Binds annotation instead to get rid of boilerplate code in your dagger module.
x: It is an adaptation of an earlier version created by Square and now maintained by Google. Dagger2 is compile time Dependency injection framework that generates code to connect dependencies at compile time.
It's officially deprecated and you can pretty much ignore it. Google's framework, which became dominant in Android ecosystem, was originally called Dagger 2. Sometimes we still refer to it as such, but, in most cases, we simply call it Dagger today.
Dagger generates code similar to what you would have written manually. Internally, Dagger creates a graph of objects that it can reference to find the way to provide an instance of a class. For every class in the graph, Dagger generates a factory-type class that it uses internally to get instances of that type.
Unlike with constructor injection, the @Inject
annotated fields of dependencies constructed in @Provides
methods can't be automatically injected. Being able to inject fields requires a component that provides the type of the field in its modules, and in the provider methods themselves, such an implementation is not available.
When the presenter
field is injected in MainActivity
, all that happens is the provider method is called and presenter
is set to its return value. In your example, the no-args constructor does no initialization, and neither does the provider method, so no initialization takes place.
The provider method does however have access to instances of other types provided in the module via its parameters. I think using parameters in the provider method is in fact the suggested (even the only) way to "inject" the dependencies of the provided type, because it explicitly indicates them as dependencies within the module, which allows Dagger to throw an error at compile-time if they can't be satisfied.
The reason it doesn't currently throw an error is because MainPresenterImpl
could get its OkHttpClient
dependency satisfied if MainPresenterImpl
and not MainPresenter
was somewhere a target for injection. Dagger can't make a members-injection method for the interface type, because as an interface, it can't have injectable fields, and it won't automatically inject the fields of the implementing type, because it's just supplying whatever the provider method returns.
You could be able to inject your MainPresenterImpl
using constructor injection.
/* unscoped */
public class MainPresenterImpl implements MainPresenter {
@Inject
OkHttpClient client;
@Inject
public MainPresenterImpl() {
}
@Override public void doStuff() {
// this.client is now available! :)
}
}
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
@Provides
/* unscoped */
public MyApplication application() {
return application;
}
}
@Module
public abstract class MainActivityModule {
@Binds public abstract MainPresenter mainPresenter(MainPresenterImpl mainPresenterImpl);
}
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