I have a beginner level question regarding dagger in android. Kindly see the snippet for a quick grasp.
The goal is to provide CentralRepo instance in my view model class via Dependency Injection.
Say, I have the following classes with dependency as below,
MyViewModel -> ctor( //initing.. mCentralRepo = CentralRepo.getInstance(RemoteRepo.getInstance) );
CentralRepo -> cror (#para RemoteRepo)
Now, these are my module classes
MyRemoteRepositoyModule
#Module
public class MyRemoteRepositoryModule {
/**
* Method to provide an instance of {#link RemoteRepository}
*
* #return RemoteRepository.
*/
#Singleton
#Provides
RemoteRepository provideRemoteRepository() {
return RemoteRepository.getInstance();
}
}
MyCentralRepositoryModule
#Module
public class MyCentralRepositoryModule {
private RemoteRepository mRemoteRepository;
public MyCentralRepositoryModule(RemoteRepository remoteRepository) {
mRemoteRepository = remoteRepository;
}
/**
* Method to provide an instance of {#link CentralRepository}
*
* #return MyCentralRepository.
*/
#Singleton
#Provides
MyCentralRepository provideMyCentralRepository() {
return MyCentralRepository.getInstance(mRemoteRepository);
}
}
and lastly, Component class (IMyComponent)
#Singleton
#Component(modules = {MyRemoteRepositoryModule.class,
MyCentralRepositoryModule.class})
public interface IMyComponent {
/**
* Targeting injection in VM.
*/
public void injectInViewModel(MyAndroidViewModel viewModel);
}
and Application class
public class MyApplication extends Application {
private IMyComponent mComponent;
#Override
public void onCreate() {
super.onCreate();
// doubtful?
mComponent = DaggerIMyComponent.builder().myRemoteRepositoryModule(new MyRemoteRepositoryModule()).build();
//
}
}
Now, If I try to inject this in my view model class
I got an error
MyCentralRepositoryModule must be set
I am sure I have missed something, It'd be appreciated If you could point out the mistake.
your IMyComponent defines that you will only gonna inject those modules in MyAndroidViewModel, and you are building your component in Application context. now it has two possibilities.
1 - Either change the parameter of your component method from MyAndroidViewModel to Application
#Singleton
#Component(modules = {
MyRemoteRepositoryModule.class,
MyCentralRepositoryModule.class
})
public interface IMyComponent {
public void injectInViewModel(Application application);
}
then use this block
DaggerIMyComponent.builder().myCentralRepositoryModule(new MyRemoteRepositoryModule()).build().injectInViewModel(this);
2- Or Build the component in MyAndroidViewModel by adding below code in class
init{
DaggerIMyComponent.builder().myCentralRepositoryModule(new MyRemoteRepositoryModule()).build().injectInViewModel(this);
}
Related
I am creating UI tests. In order not to interact with the real server, I use MockWebServer. My goal is to emulate various server responses and see how the program as a whole will respond to them. At the moment, I don’t understand how to open screens that require authorization. Of course, I can write a code that will login to the authorization screen, and then go to the desired window. But this requires additional time to complete the test, and I would like to avoid this. I would not want to mocking classes, because I need to check the production version of the application. How can i do this?
For DI, I use Dagger-2. Here is the component code:
#Singleton
#Component(modules = {
AvatarsModule.class,
EncryptionModule.class,
ApiModule.class,
WalletsModule.class,
GeneralModule.class,
InteractorsModule.class,
PushNotificationsModule.class,
AppModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder context(Context context);
AppComponent build();
}
void inject(App app);
}
Here is the class code in which the authorization state is stored:
public class ApiWrapper {
private Api api;
private KeyPair keyPair;
private Account account;
...
public Flowable<Authorization> authorize(KeyPair tempKeyPair) {
return api
.authorize(tempKeyPair.getPublicKeyString().toLowerCase())
.subscribeOn(Schedulers.io())
.doOnNext((authorization -> {
this.account = authorization.getAccount();
this.keyPair = tempKeyPair;
}));
}
...
}
If anyone is still interested. I wrote an InstrumentationTestFacade class in which I put an ApiWrapper object using Dagger. Next, the InstrumentationTestFacade is injected into the Application object. Since the application object is not a singleton, there is no leak of responsibility in the main code, but from the test code you can access this facade using the following code:
Application application = (Application) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
InstrumentationTestFacade facade = application.getInstrumentationTestFacade();
Below is an example:
public class InstrumentationTestFacade {
private LogoutInteractor logoutInteractor;
private SecurityInteractor securityInteractor;
private ApiWrapper apiWrapper;
public InstrumentationTestFacade(
LogoutInteractor logoutInteractor,
SecurityInteractor securityInteractor,
ApiWrapper apiWrapper
) {
this.logoutInteractor = logoutInteractor;
this.securityInteractor = securityInteractor;
this.apiWrapper = apiWrapper;
}
public void logout() {
logoutInteractor.logout();
}
public ApiWrapper getApiWrapper() {
return apiWrapper;
}
public SecurityInteractor getSecurityInteractor() {
return this.securityInteractor;
}
}
public class Application extends MultiDexApplication implements HasActivityInjector, HasServiceInjector {
...
#Inject
InstrumentationTestFacade instrumentationTestFacade;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.context(this)
.build()
.inject(this);
}
...
public InstrumentationTestFacade getInstrumentationTestFacade() {
return instrumentationTestFacade;
}
}
Using Dagger 2 for the first time with MVP.
I am stuck at a very simple implementation.
my presenter module takes View Interface in constructor along with context and data manager,I am confused in how to send activity context to the constructor for the view interface..
Any help will be highly appreciated..
Here is my code for App class:
public class App extends Application {
private static App app;
public SampleComponent getSc() {
return sc;
}
private SampleComponent sc;
public static App getApp() {
return app;
}
#Override
public void onCreate() {
super.onCreate();
app = this;
sc = DaggerSampleComponent.builder()
//.sampleModule(new SampleModule())
.presenterModule(new PresenterModule(new MainActivity(), getApplicationContext(), new ModelManager()))
.build();
}
}
Code for Presenter Module :
#Module
public class PresenterModule {
ShowCountContract.view v;
ModelManager mm;
Context c;
public PresenterModule(MainActivity m, Context c,
ModelManager mm) {
this.c = c;
this.mm = mm;
this.v = m;
}
#Singleton
#Provides
PresenterClass getPresentationClass() {
return new PresenterClass(mm, v);
}
}
To handle the Android context the best approach is to create an Application Component with an Application Module. This module should be responsible to provide objects that are common in the entire application, as the Context. And based on that component you can create subcomponents for each feature/activity/etc.
#Module
public class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
Context provideContext() {
return application;
}
}
If you choose to work with just one component (what I do not recommend), your code for DaggerComponent creation will look like this:
DaggerSampleComponent.builder()
.applicationModule(new ApplicationModule(this))
.otherModule(new OtherModule())
.build();
Or you can use Component.Builder
As the Activity instance is created by the Android Framework, we cannot pass the View interface as a constructor parameter. The common way is to create such a method as attachView(ViewInterface) in your Presenter to be able to set an internal property.
Another thing you should change is to remove the Presenter's constructor from App and let the OtherModule be responsible for that:
#Module
public class OtherModule {
#Singleton
#Provides
PresenterClass getPresentationClass(Context ctx) {
return new PresenterClass(ctx, new ModelManager());
}
}
I recommend you to check this article where it goes deeper on Dagger explanation and even shows another Dagger's version that is directly thought to the Android environment.
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);
}
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
}
}
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 {
}
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.