DI with Dagger2, add to or get from the graph? - android

Using Dagger2, what are the pros and cons of the 2 following implementations:
1)
public class MyReceiver extends BroadcastReceiver {
Something something;
#Override public void onReceive(Context context, Intent intent) {
something = ((MyApplication) context.getApplicationContext())
.getComponent().getSomething();
}
}
#Component
public interface MyComponent() {
Something getSomething();
}
2)
public class MyReceiver extends BroadcastReceiver {
#Inject Something something;
#Override public void onReceive(Context context, Intent intent) {
((MyApplication) context.getApplicationContext())
.getComponent().inject(this);
}
}
#Component
public interface MyComponent() {
void inject(MyReceiver myReceiver);
}

tl;dr Go for Option 2. #Inject your fields.
If your object can be created by injecting into the constructor (sample below), definitely go for Option 2, since you will not have to write any code other than marking the constructor to use with #Inject.
// object can just be injected without any additional modules
// since all dependencies can be satisfied
public class Something {
#Inject
public Something() {}
}
So to further answer the question let us concentrate on field / method injection.
Just using #Inject annotations will keep your code simpler. Option 2 will let you again just declare dependencies.
// dont't worry about where they come from
// or how they are created
#Inject
Something mSomething;
#Inject
Foo mFoo;
public void onCreate() {
((Application) context.getApplicationContext())
.getComponent().inject(this);
}
Also, for testing purposes you might want to override the injected fields, by just injecting the same object again with your TestComponent. This also is only possible with Option 2.
#Test
void someTest() {
// this is only possible with #Injected annotated fields
testComponent.inject(activityUnderTest);
}
But the first option!
The 1. Option will mostly be used to expose dependencies for dependent components, not to be confused with SubComponent components, which will always have full access to the parent graph.
#Component
public interface MyComponent() {
Something getSomething(); // exposes something
}
#Component(dependencies = {MyComponent.class})
public interface OtherComponent() {
// can access something in modules
}
Using Option 1 manually would mean to handle the dependency injection yourself by programmatically fetching dependencies from actual business code and writing harder to test code, which you are trying to avoid by using DI.

Related

How can I replace Activity scoped dependencies with mocks using Dagger2

I have a scoped dependency in my Activity and I want to test that activity with some mocks. I have read about different approach that suggest to replace Application component with a test component during the test, but what I want is to replace the Activity component.
For example, I want to test the Activity against mock presenter in my MVP setup.
I believe that replacing component by calling setComponent() on Activity will not work, because Activity dependencies already injected via field injection, so during the test, real object will be used.
How can I resolve this issue? What about Dagger1? Is it has the same issue?
Injecting the Component
First, you create a static class to act as a factory for your Activity. Mine looks a little like this:
public class ActivityComponentFactory {
private static ActivityComponentFactory sInstance;
public static ActivityComponentFactory getInstance() {
if(sInstance == null) sInstance = new ActivityComponentFactory();
return sInstance;
}
#VisibleForTesting
public static void setInstance(ActivityComponentFactory instance) {
sInstance = instance;
}
private ActivityComponentFactory() {
// Singleton
}
public ActivityComponent createActivityComponent() {
return DaggerActivityComponent.create();
}
}
Then just do ActivityComponentFactory.getInstance().createActivityComponent().inject(this); inside your Activities.
For testing, you can replace the factory in your method, before the Activity is created.
Providing mocks
As #EpicPandaForce's answer makes clear, doing this the officially-supported way currently involves a lot of boilerplate and copy/pasted code. The Dagger 2 team need to provide a simpler way of partially overriding Modules.
Until they do though, here's my unnoficial way: Just extend the module.
Let's say you want to replace your ListViewPresenter with a mock. Say you have a PresenterModule which looks like this:
#Module #ActivityScope
public class PresenterModule {
#ActivityScope
public ListViewPresenter provideListViewPresenter() {
return new ListViewPresenter();
}
#ActivityScope
public SomeOtherPresenter provideSomeOtherPresenter() {
return new SomeOtherPresenter();
}
}
You can just do this in your test setup:
ActivityComponentFactory.setInstance(new ActivityComponentFactory() {
#Override
public ActivityComponent createActivityComponent() {
return DaggerActivityComponent.builder()
.presenterModule(new PresenterModule() {
#Override
public ListViewPresenter provideListViewPresenter() {
// Note you don't have to use Mockito, it's just what I use
return Mockito.mock(ListViewPresenter.class);
}
})
.build();
}
});
...and it just works!
Note that you don't have to include the #Provides annotation on the #Override method. In fact, if you do then the Dagger 2 code generation will fail.
This works because the Modules are just simple factories - the generated Component classes take care of caching instances of scoped instances. The #Scope annotations are used by the code generator, but are irrelevant at runtime.
You cannot override modules in Dagger2 [EDIT: you can, just don't specify the #Provides annotation on the mock), which would obviously be the proper solution: just use the builder().somethingModule(new MockSomethingModule()).build() and be done with it!
If you thought mocking is not possible, then I would have seen two possible solutions to this problem. You can either use the modules to contain a pluggable "provider" that can have its implementation changed (I don't favor this because it's just too verbose!)
public interface SomethingProvider {
Something something(Context context);
}
#Module
public class SomethingModule {
private SomethingProvider somethingProvider;
public SomethingModule(SomethingProvider somethingProvider) {
this.somethingProvider = somethingProvider;
}
#Provides
#Singleton
public Something something(Context context) {
return somethingProvider.something(context);
}
}
public class ProdSomethingProvider implements SomethingProvider {
public Something something(Context context) {
return new SomethingImpl(context);
}
}
public class TestSomethingProvider implements SomethingProvider {
public Something something(Context context) {
return new MockSomethingImpl(context);
}
}
SomethingComponent somethingComponent = DaggerSomethingComponent.builder()
.somethingModule(new SomethingModule(new ProdSomethingProvider()))
.build();
Or you can bring the provided classes and injection targets out into their own "metacomponent" interface, which your ApplicationComponent and your TestApplicationComponent extend from.
public interface MetaApplicationComponent {
Something something();
void inject(MainActivity mainActivity);
}
#Component(modules={SomethingModule.class})
#Singleton
public interface ApplicationComponent extends MetaApplicationComponent {
}
#Component(modules={MockSomethingModule.class})
#Singleton
public interface MockApplicationComponent extends MetaApplicationComponent {
}
The third solution is to just extend the modules like in #vaughandroid 's answer. Refer to that, that is the proper way of doing it.
As for activity scoped components... same thing as I mentioned here, it's just a different scope, really.
I've found the following post that solves the problem:
http://blog.sqisland.com/2015/04/dagger-2-espresso-2-mockito.html
You need first to allow to modify the component of the activity:
#Override public void onCreate() {
super.onCreate();
if (component == null) {
component = DaggerDemoApplication_ApplicationComponent
.builder()
.clockModule(new ClockModule())
.build();
}
}
public void setComponent(DemoComponent component) {
this.component = component;
}
public DemoComponent component() {
return component;
}
And modify it in the test case
#Before
public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
DemoApplication app
= (DemoApplication) instrumentation.getTargetContext().getApplicationContext();
TestComponent component = DaggerMainActivityTest_TestComponent.builder()
.mockClockModule(new MockClockModule())
.build();
app.setComponent(component);
component.inject(this);
}

how can I inject a dependency in a method?

I'm a beginner with dependency injection.. specifically Dagger 2. I'm trying to figure out if/how I can do something like this:
#Inject
public void someMethodName(int someInteger, SomeObject dependency){
// do something with the dependency.
}
Or do I need to put that dependency in as a class var? any help with this would be greatly appreciated. also in this case the variable someInteger is not a dependency, but is being added by the caller... does that matter?
can I call it like this:
this.someMethodName(5);
android studio does not like the above calling method (I'm assuming because I'm doing something wrong)
You need to create component which is annotated by #Component.
The Component accepts module which provides dependencies.
Every component's name that you create starts with Dagger prefix, e.g. for MyComponent.
Let's look at the following example:
#Singleton
#Component(modules = DemoApplicationModule.class)
public interface ApplicationComponent {
void inject(DemoApplication application);
}
We created ApplicationComponent with single injection method. What we're saying is that we want to inject certain dependencies in DemoApplication.
Moreover, in the #Component annotations we specify module with provision methods.
This is like our module looks like:
#Module
public class DemoApplicationModule {
private final Application application;
public DemoApplicationModule(Application application) {
this.application = application;
}
#Provides #Singleton SomeIntegerHandler provideIntegerHandler() {
return new MySomeIntegerHandlerImpl();
}
}
What we're saying by creating DemoApplicationModule is that the module can provide desired dependencies in the injection place specified by our Component.
public class DemoApplication extends Application {
private ApplicationComponent applicationComponent;
#Inject SomeIntegerHandler handler;
#Override public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent.builder()
.demoApplicationModule(new DemoApplicationModule(this))
.build();
applicationComponent.inject(this);
handler.someMethodName(5);
}
}
See documentation what you kind of dependencies you can obtain. Additionally to obtaining just raw instance you can obtain Provider, Factory or Lazy instance.
http://google.github.io/dagger/api/latest/dagger/Component.html
You can also create scoped dependencis, the lifecycles of which depend on the lifecycle of injection places, like Activities or Fragments.
Hope I gave you the basic notion of what Dagger is.
YOU CAN USE SOME INTERFACE
public interface myDependence{
int myFunction(int value);
}
NOW IMPLEMENT IN YOU CLASS
public myClass implements MyDependence{
#Override
int myFunction(int value){
// do something
}
}

Dagger2 - null instead of injected object

For making things simple, suppose I want to inject EmailValidator from apache validators into my activity:
public class MainActivity extends FragmentActivity {
#Inject
EmailValidator emailValidator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
I have a MainModule class:
#Module
public class MainModule {
#Provides
public EmailValidator providesEmailValidator() {
return EmailValidator.getInstance();
}
}
and MainComponent interface:
#Singleton
#Component(modules = MainModule.class)
public interface MainComponent {
EmailValidator getEmailValidator();
}
When trying to use my validator in activity, I'm getting a nullpointer exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean org.apache.commons.validator.routines.EmailValidator.isValid(java.lang.String)' on a null object reference
Obviously I'm missing something. I know that dagger creates component implementation for me. Should I use it? How?
If I do the following in my onCreate method:
emailValidator = Dagger_MainComponent.create().getEmailValidator();
then everything works fine.
But I want to be able to use #Inject annotation anywhere (probably on setter/constructor instead of a field) instead.
What am I missing?
I did something similar with dagger1 and it worked. Of course I needed to call ObjecGraph.inject(this) in activity. What's the dagger2 equivalent?
EDIT:
Ok, so I've found a solution. If anyone will ever have such a problem, there are some snippets:
1) I've created an application class:
public class EmailSenderApplication extends Application {
private MainComponent component;
#Override
public void onCreate() {
super.onCreate();
component = Dagger_MainComponent
.create();
component.inject(this);
}
public MainComponent component() {
return component;
}
}
2) In AndroidManifest.xml:
<application
android:name=".EmailSenderApplication"
...
3) And finally, in the activity class where I want to inject some components those two ugly as hell lines:
component = ((EmailSenderApplication) getApplication()).component();
component.inject(this);
Looks like you need to build your component as in:
component = Dagger_ MainComponent.builder()
.mainModule(new MainModule())
.build();
Typically, you do this in the onCreate method of your Application.
One good resource that may help you is the example apps in the Dagger 2 repo.
I also found this PR helpful, from a suggested update to Jake Wharton's u2020 sample app (from the main Dagger 2 Engineer). It gives a good overview of the changes you need to make when going from Dagger 1 to 2 and, apparently that's what he points people to as well.

What does #Module means in dagger for android?

I have read many blogs but still i am not able to figure out #Module annotation functioning in dagger.
#Inject i got that it provides dependency injection at runtime. But what does #Module does.
since the object graph is also built on module.
For ex i.e i have this snippet of code from https://github.com/AndroidBootstrap/android-bootstrap.
#Module(
complete = false,
injects = {
BootstrapApplication.class,
BootstrapAuthenticatorActivity.class,
MainActivity.class,
BootstrapTimerActivity.class,
}
)
public class BootstrapModule {
}
so what does it basically does. since i am also trying to build one application using dagger as dependency injection for android.But since I am not able to get #Module concept clearly I am just stuck.
Can anyone please help me out with some basic example or concept. I think this will be helpful for all who is using dagger.
If you have a look to the docs for the annotation, a #Module annotated class defines a class that contributes to the dagger object graph. In the Spring framework for example, the equivalent would be the #Configuration anntotation. It defines a configuration point for your object graph, where you declare which objects you want to be available for injection and their scopes.
As a simple example, let's say we want a singleton object to be used by any Activity in the app. It has to be created in the module:
#dagger.Module(injects = {MyActivity.class})
public class Module {
#Provides
#Singleton
public MySinletonBean provideMySingleton() {
return new MySinletonBean();
}
}
This will create a MySingleton object which can be injected in MyActivity. This is a very basic example, but we can perform other actions in the graph there, like using dependencies in the constructors:
#dagger.Module(injects = {MyActivity.class})
public class Module {
private DependencyBean dependency = new DependencyBean();
#Provides
#Singleton
public MySinletonBean provideMySingleton() {
return new MySinletonBean(dependency);
}
#Provides
#Singleton
public MySinletonBean provideMyOtherSingleton() {
return new MyOtherSinletonBean(dependency);
}
}
Then, in MyActivity we need to access the graph for the application in the onCreate method:
#Inject
MySingletonBean singleton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
((MyApplication) getApplication()).getGraph().inject(this);
}
So, who does create the object graph here? The MyApplication class does it when your application starts (don't forger to add it in your androidManifest.xml):
public class MyApplication extends Application {
private ObjectGraph graph;
public ObjectGraph getGraph() {
return graph;
}
#Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(new Module(this));
graph.inject(this);
}
}
So the execution flow in a dagger app would be:
The android app starts and the MyApplication class builds the graph, parsing the #Module annotated classes and keeping an instance of it.
Then, the classes declared in the module can access its objects just injecting themselves in the object graph. Gradle then will evaluate their #Inject annotations and perform the dependency injections.
I guess Annotation Processing Tool requires that to generate code at the compile time.
This makes it Dagger can provide validation at compile time and not only at runtime.

Dagger not initializing injected field in Android

Started introducing Dagger into my App and I'm having problems getting a very basic field initialized. Here's a reduced version of my code:
#Inject public DaggerUtils daggerUtils;
public class AppState extends Application {
#Override
public void onCreate() {
super.onCreate();
// Set up Dagger
AppModule appModule = new AppModule();
mObjectGraph.create(appModule);
daggerUtils.print();
}
}
Module used:
#Module(
injects = { AppState.class}
)
public class AppModule {
// This provides method is commented out because from what I can understand from the Dagger documentation
// Dagger should automatically take care of calling the constructor I have provided
// with the #Inject annotation. I have tried commenting this out as well and it still
// fails.
//#Provides
//DaggerUtils provideDaggerUtils() {
// return new DaggerUtils();
//}
}
Basic util class:
public class DaggerUtils {
#Inject
public DaggerUtils() {
}
public void print(){
Logger.e("Dagger", "printed instantiated");
}
}
So from what I understand because I have the #Inject annotation before the DaggerUtils constructor and the #Inject annotation before the DaggerUtils instance I'm using in my AppState class, Dagger should take care of initializing the DaggerUtils instance without me having to call the constructor. However it keeps giving me an NullPointerException when I try to call daggerUtils.print() (Line 12 in AppState class). Why is dagger not initializing DaggerUtils? I feel like I'm missing something very basic here. I've also tried using the #Provides method commented out in the AppModule to provide an instantiated DaggerUtils but it still isn't working.
I had same problem this evening.
For every class, who needs injection you must call:
mObjectGraph.create(appModule).inject(this);
That is usefull to create inject method in Application.
public void inject(Object object) {
mObjectGraph.inject(object);
}

Categories

Resources