How to provide parameter to constructor while doing constructor injection? - android

I am using dagger2 framework for dependency injection.
How to create instance of dependency objectB (AND INJECT) with a constructor injection. Class B has a parameter constructor.
Please find the example below.
//This is class A, which is dependent on Class B.
//This has to create instance of B, with a (int) parameter.
class A {
#Inject
public B objectB;
public A(int some_value){
//This one I want to make it through dagger2
//objectB = new B(some_value);
BComponent bComponent = DaggerBComponent.builder().build();
bComponent.provideB(this);
}
}
//This is Class B, which has constructor injection.
//Constructor has a int parameter.
class B {
#Inject
public B(int var){
other_method(var);
}
}
//This is dagger module, which provides Object B
#Module
public class BProvider {
#Provides
public B getB() {
return new B();
}
}
//This is dagger component, provide class-B dependency to class-A
#Component(modules = {BProvider.class})
public interface BComponent {
void provideB(A objA);
}

From the elaboration in the comments:
I am trying constructor injection to achieve dependency. ClassA depends on ClassB. And ClassB has parameter constructor, say ClassB(int param). This parameter is provided at runtime, so my confusion is how dagger is able to create object B.
If B's param is the same every time, across your object graph
This might be the case if your param is a key, ID, or other configuration setting. If so, you can use a #Provides method in a Module to teach Dagger how to provide a ClassB object when you ask for one.
#Module
public abstract class YourModule {
#Provides static ClassB provideClassB() {
return new ClassB(/* your constant here */);
}
}
Because #Provides methods can take parameters that come from the object graph, you can even get your value from somewhere else:
#Module
public abstract class YourModule {
#Provides static ClassB provideClassB(SomeDependendency someDep) {
return new ClassB(someDep.getParameterForClassB());
}
}
If B only takes custom parameters
This might be the case if your ClassB is a data object or disposable object, especially if you don't need to replace ClassB for testing. (Don't mock data objects.) If all of that is true, then ClassB doesn't need to be a part of your object graph. Call the constructor directly.
ClassB myB = new ClassB(someParameter);
Sometimes this distinction is called injectables versus newables: If you have complicated and interconnected classes that need to access one another, you have an injectable, and references to it should be provided externally. If you have a tightly-contained class that doesn't need substitution, you have a newable, and can call the constructor or static factory method directly.
If B has injected parameters AND constructor parameters
In some cases, a class like ClassB needs both one-off constructor parameters and injected parameters from the graph at the same time. Though this might be a subtle hint that the class is doing too much, there are common workarounds that combine those two inputs into the same list of constructor parameters. This is known as assisted injection, and allows for the creation of factory objects where the DI framework provides the factory and you use it to get the value you want.
/* in ClassB */ public ClassB(DepA a, DepB b, int param) { /* ... */ }
/* ClassBFactory */
public ClassBFactory {
Provider<DepA> depAProvider;
Provider<DepB> depBProvider;
public ClassBFactory(
Provider<DepA> depAProvider,
Provider<DepB> depBProvider) {
/* set depAProvider and depBProvider */
}
public ClassB get(int param) {
return new ClassB(depAProvider.get(), depBProvider.get(), param);
}
}
Because this is similar boilerplate-generation to Dagger, but more reusable in other dependency injection frameworks, Dagger recommends the Google project AutoFactory to generate that implementation based on inspecting the dependencies and classes. You'll need to add some annotations to ClassB to get that to work, but it will allow you to distinguish between graph-provided deps and one-off deps, and insulate you from having to change constructor calls if your dep list changes.

Related

Can injecting a field member with Dagger be done with only a constructor

I inject a field member as follows:
#Inject
lateinit var repository: Repository
What I have noticed is that repository will only get set if I use a component builder:
init {
DaggerLoginViewModelComponent
.builder()
.build()
.inject(this)
}
But I have read in several places that the field member will get set even if you just mark the constructor of the class you want instantiated with Inject:
class Repository #Inject constructor() {
}
But I have not found this to be true. Even with the constructor having the Inject annotation present, I still need to run the builder. Can someone confirm whether this is true or not.
The way field injections work is through a supplemental method on the Dagger component, in this case inject(this).
If you look at the Dagger generated code, you'll see that inject() method's body contains the code to perform the member injection.
Here's how the generated code would look-
#Override
public void inject(Foo instance) {
injectFoo(instance);
}
private injectFoo(Foo instance) {
Foo_MembersInjector.injectRepository(instance, repositoryProvider.get());
return instance;
}
...
public final class Foo_MembersInjector implements MemberInjector<Foo> {
public static void injectRepository(Foo instance, Repository repository) {
instance.repository = repository // <-- this is where the actual injection happens
}
}
You're right when you say that Dagger knows how to construct the Repository object, but it doesn't know that this object has to be injected in a separate class. And that only happens when you call the inject() method with the instance of specific type, which is how it generates code to perform the injection.
Hope this helps.

android retrofit2, dagger2 unit test

I learn how to test the presenter layer of MVP architecture in android, my presenter using retrofit 2 and in my activity I used dagger 2 as dependency injection to my presenter, this is my Dagger and presenter injection looks like:
#Inject
AddScreenPresenter addScreenPresenter;
This is the Dagger builder :
DaggerAddScreenComponent.builder()
.netComponent(((App) getApplicationContext()).getNetComponent())
.addScreenModule(new AddScreenModule(this, new ContactDatabaseHelper(this)))
.build().inject(this);
and this is my presenter constructor :
#Inject
public AddScreenPresenter(Retrofit retrofit, AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper)
{
this.retrofit = retrofit;
this.view = view;
this.contactDatabaseHelper = contactDatabaseHelper;
}
I have write the unit test class and mock the Retrofit class, but when I run it, the error appears :
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
This is the test class :
#RunWith(MockitoJUnitRunner.class)
public class AddScreenPresenterTest {
private AddScreenPresenter mAddPresenter;
#Mock
private Retrofit mRetrofit;
#Mock
private Context mContext;
#Mock
private AddScreenContact.View mView;
#Mock
private ContactDatabaseHelper mContactDatabaseHelper;
String firstName, phoneNumber;
Upload upload;
#Before
public void setup() {
mAddPresenter = new AddScreenPresenter(mRetrofit, mView, mContactDatabaseHelper);
firstName = "aFirstName";
phoneNumber = "998012341234";
Uri path = Uri.parse("android.resource://"+BuildConfig.APPLICATION_ID+"/" + R.drawable.missing);
upload = new Upload();
upload.title = firstName;
upload.description = "aDescription";
upload.albumId = "XXXXX";
upload.image = new File(path.getPath());
}
#Test
public void checkValidationTest() {
verify(mAddPresenter).checkValidation(firstName, phoneNumber);
}
#Test
public void uploadMultiPartTest() {
verify(mAddPresenter).uploadMultiPart(upload);
}
}
this is my module :
#Module
public class AddScreenModule {
private final AddScreenContact.View mView;
private final ContactDatabaseHelper mContactDatabaseHelper;
public AddScreenModule (AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper)
{
this.mView = view;
this.mContactDatabaseHelper = contactDatabaseHelper;
}
#Provides
#CustomScope
AddScreenContact.View providesAddScreenContactView() {
return mView;
}
#Provides
#CustomScope
ContactDatabaseHelper providesContactDatabaseHelper() {
return mContactDatabaseHelper;
}
}
I know that Retrofit class is a final class, and now I stuck and don't know how to create the presenter object in my test class. Please help me, how to create the object of the presenter class with retrofit in the constructor. Feel free to ask if my question is not clear enough, and thank you very much for your help.
Personally I'd make the presenter not depend on the Retrofit class but rather on the services created by Retrofit - These are mockable.
It's hard to say from the code you posted which services your presenter actually uses, but for the sake of simplicity let's say it uses only one and let's say it's AddsService - This is an interface ready to work with Retrofit. Something like this for example
public interface AddsService {
#GET(...)
Call<List<Adds>> getAllAdds();
}
Now you can make your presenter depend on this rather than Retrofit
#Inject
public AddScreenPresenter(AddsService addsService,
AddScreenContact.View view,
ContactDatabaseHelper contactDatabaseHelper){
this.addsService = addsService;
this.view = view;
this.contactDatabaseHelper = contactDatabaseHelper;
}
You now need to provide this dependency. I'm guessing you have also a NetModule since you have a NetComponent, so I assume you can just do:
#Module
public class NetModule {
// Methods providing Retrofit
#Provides
#Singleton
public AddsService providesAddsService(Retrofit retrofit) {
return retrofit.create(AddsService.class);
}
}
Notice how the providesAddsService depends on retrofit? This should be already provided since your presenter is depending on it. You shouldn't need to change anything for that. Dagger is able to figure out how to provide Retrofit to the method providesAddsService.
Please notice also that I'm assuming you can provide these in a Singleton scope. I assume this because in your code you retrieve the component from the application, which should handle the singleton scope.
Now in your tests you can simply mock AddsService and test your presenter.
If your presenter depends on more services, I'd also pass them in the constructor and provide the implementations with Dagger.
As a bonus, let me also say that the retrofit instance and the retrofit services should only be created once (or at least as less times as possible). This is because they're usually expensive operations and you usually always query the same endpoints with different parameters.
EDIT
To answer some of the questions in the comments. First the easy one: How to create the presenter in the test classes? Like you I too try to get away from Dagger during tests, that's why I prefer constructor dependency injection just like you show you're using. So in my test class I'd have something very similar like you:
#RunWith(MockitoJUnitRunner.class)
public class AddScreenPresenterTest {
private AddScreenPresenter mAddPresenter;
#Mock
private AddsService addsService;
// ...
#Before
public void setUp() throws Exception {
mAddPresenter = new AddScreenPresenter(addsService,
mView, mContactDatabaseHelper);
// ...
}
}
So basically the only difference is that I would pass the mock to the service.
Now the second question: How to call the presenter constructor from the activity? Well you don't... that's the whole idea of dependency injection. You should use dagger to provide your presenter. I think this is already what you do and I guess this is what it's in your activity:
#Inject
AddScreenPresenter addScreenPresenter;
So all you need to do is have a provider method in your module that provides this and is able to inject it.
You can also make the component return the presenter provided by the module:
#Component(...)
public interface AddScreenComponent {
AddScreenPresenter getPresenter();
}
And then in your activity you'd do something like:
addScreenPresenter = component.getPresenter();
I don't really have any preference here. The key point is to understand that you should not build the objects yourself (unless inside #Modules). As a rule of thumb any time you see new being used that means you have a tight dependency on that object and you should extract it to be injected. So this is why you should avoid creating the presenter inside your activity. It will couple the presenter to the activity.

Provide method dependendies

In the documentation it says that #Provides methods may have dependencies on their own, like:
#Provides Pump providePump(Thermosiphon pump) {
return pump;
}
What would change if I would write it like that:
#Provides Pump providePump() {
return new Thermosiphon();
}
And in the first snipped: Where does the method get its pump from?
The documentation also shows the Thermosiphon class:
class Thermosiphon implements Pump {
private final Heater heater;
#Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
...
}
The constructor of this class is annotated with #Inject. This lets Dagger know to use this Constructor whenever a Thermosiphon is necessary, and automatically supplies a Heater instance to it, so you don't have to.
It is perfectly fine for you to create a new Thermospihon instance yourself, but Dagger saves you the trouble by doing it like this. For example, you would need to get some Heater reference from somewhere if you do it manually. That is what Dagger is all about, so you don't have to do the tedious repetitive work.
These are effectively the same IF a new instance of Thermosiphon.class is created every time you request an instance. If it's a singleton (or scoped in any way), then we have a difference.
If you have the following, then the first example is an alias to the singleton. The second example, however, will still create new instances every time.
#Provides
#Singleton
Thermosiphon provideThermosiphon() {
return new Thermosiphon();
}
Personally, I like the first approach better. Using the first approach, you could add or alter the provider later and adjust the scope or the state of the instance before it'a passed to the alias. It seems a little more flexible.
It looks for other beans declared in your module
For example:
#Module
public class MainModule {
#Provides
public EmailServiceApiGateway provideEmailServiceApiGateway() {
return new EmailServiceApiGateway();
}
#Provides
public EmailSendingActivityPresenter provideEmailSendingActivityPresenter(EmailServiceApiGateway emailServiceApiGateway) {
return new EmailSendingActivityPresenterImpl(emailServiceApiGateway);
}
}
So in the case above, EmailServiceApiGateway gets automatically injected into EmailSendingActivityPresenter.

In Dagger are Singletons within the sub-graph cached or will they always be recreated when a new activity sub-graph is constructed?

I'm using Dagger to create activity specific object graphs. Within this subgraph, I make use of a Singleton MyPresentationModel.
When i exit my activity, and enter the activity again, my expectation is that a new instance of the activity specific object graph is created, which in turn would create a new instance of Singleton MyPresentationModel (by virtue of the #Singleton semantic per Dagger. See this So answer for specifics) which would then last for the life of the activity specific object graph.
However, this is not what i'm observing, every time the activity specific object graph is created, the same instance of MyPresentationModel is used. I added a debug point into the constructor of MyPresentationModel. The very first time we enter the constructor. Subsequently even on activity exits and reentries, we don't enter the constructor (and because of this the UserSession being used within my Presentation model uses the old value from the very first constructor injection).
While i can technically solve the problem by re-setting UserSession inside MyPresentaitonModel with an external public setter, I want to understand better the mechanics of the activity specific object graph creation/destruction.
By nullifying the graph in my onDestroy, does that still mean that there is a possibility of the Singletons within my subgraph being reused at a later point ? (possibly until they are truly GCed?)
Here's some code:
// MyAppModule
#Module(
includes = { UserSession.class},
injects = { MyApplication.class })
public class MyAppModule {
private final MyApplication _app;
MyAppModule(MyApplication app) {
_app = app;
}
// ...
}
// Main Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_activityObjectGraph = MyApplication.get()
.getObjectGraph()
.plus(Arrays.<Object>asList(new SubModule()).toArray());
// Inject ourselves so subclasses will have dependencies fulfilled when this method returns.
_activityObjectGraph.inject(this);
}
#Override
protected void onDestroy() {
_activityObjectGraph = null;
// this eagerly allows GC, but doesn't necessarily destroy the subgraph ?
super.onDestroy();
}
// SubModule
#Module(injects = { MyPresentationModel.class, MainActivity.class },
addsTo = MyAppModule.class,
library = true)
public class SubModule {}
}
// MyPresentationModel
#Singleton
public class MyPresentationModel {
private UserSession _session;
#Inject
public MyPresentationModel(UserSession session) {
_session = session;
}
public void someMethodThatUsesSessionInfo() {
// _session.getUser() ...
}
}
#weefbellington posted a very informative answer, but reading it made me realize my question was not specific and clear enough. Here's attempt 2:
MyAppModule (main graph) -> provides a Singleton UserSession
MySubModule (sub graph plused onto MyAppModule) -> provides "activity specific" Singleton MyPresentationModel which requires a UserSession (provided my MyAppModule) on construction.
I now close the activity, destroying MySubModule (and also hopefully MyPresentationModel which is a Singleton), I update UserSession with some new information.
I open MainActivity again, thus re-creating the sub-graph from MySubModule, which inturn provides a MyPresentationModel.
The issue I'm noticing is that MyPresentationModel which is the local Singleton is not being reconstructed again i.e. this part of the code:
#Inject
public MyPresentationModel(UserSession session) {
_session = session;
}
is only ever being called once. My expectation was that this part of the code would be run again, and the UserSession would be pulled again from the Main graph and since it was updated, it would hold the updated values. My question is: are Singletons within the sub-graph cached in anyway or will they always be recreated when a new activity sub-graph is spawned?
How MyPresentationModule is injected depends on how your modules are specified. For example, assume that you are injecting the class Foo:
public class Foo {
private final MyPresentationModel model;
#Inject
public Foo(MyPresentationModel model) {
this.model = model;
}
}
If your modules are structured like (A), then the MyPresentationModel singleton will be injected into Foo by the main object graph:
EXAMPLE A
#Module(injects = { Foo.class })
public class MainModule { ... }
#Module(addsTo = MainModule.class, injects = { MyPresentationModel.class })
public class SubModule { ...}
Alternatively, if your modules are structured like (B), then the MyPresentationModel singleton will be injected into Foo by the subgraph:
EXAMPLE B
#Module
public class MainModule { ... }
#Module(addsTo = MainModule.class, injects = { Foo.class, MyPresentationModel.class })
public class SubModule { ... }
In your particular case, since you have specified that MyAppModule injects MyApplication, I would guess that you are trying to inject MyPresentationModel into your Application class. This is probably not what you want to do. You probably want inject this into your Activity class using the submodule, as in (C).
EXAMPLE C
#Module(injects = { MainActivity.class, MyPresentationModel.class },
addsTo = MyAppModule.class,
library = true)
public class SubModule { ... }
public class MainActivity {
#Inject MyPresentationModel presentationModel;
...
}
If you do this the MyPresentationModel singleton will be bound to the Activity subgraph instead of the main graph, and should be disposed when the Activity is destroyed.
Once you have a handle on Dagger, you might want to check out Mortar, which gives you finer-grained control over creation and destruction of ObjectGraph subscopes.

Dagger not injecting Android Annotations class

Alright, I'm having an issue trying to mix frameworks.
So, I have a #SharedPref annotated class that should generate a Shared Preferences manager from Android Annotations. The class looks a bit something like this:
DownloadPrefs.java
#SharedPref(value= SharedPref.Scope.UNIQUE)
public interface DownloadPrefs {
#DefaultBoolean(false)
boolean hasEnabledDownload();
#DefaultBoolean(false)
boolean showedDownloadDialog();
#DefaultLong(0)
long downloadRefreshedOn();
}
Now, I'd like to inject the resulting class (which will be DownloadPrefs_) into a Fragment to make use of it. The fragment has had working injection before adding the new module, so I'm only going to write here what I added:
Fragment.java
#Inject DownloadPrefs_ downloadPrefs;
Now, since the actual DownloadPrefs_ class is generated at runtime, it would make the most sense to create an #Provides annotation for it, since I can't mark a constructor as injected. Nor does the DownloadPrefs_ have a no-arg constructor. The module I'm using then receives the new #Provides:
DownloaderModule.java
#Provides //#Singleton // Does not work with/out #Singleton
DownloadPrefs_ provideDownloadPrefs() {
return new DownloadPrefs_(MinimalBible.getApplication());
}
To be technical about it, the DownloadPrefs_ constructor that gets generated by Android Annotations expects a Context passed to it, I would have guessed that the Application context would be suitable. Otherwise, I'm not sure how I could possibly get access to the Activity context. Or whether that would actually break the ObjectGraph.
However, when I go to run the actual injection, I get the following message:
Caused by: java.lang.IllegalStateException: Errors creating object graph:
org.bspeice.minimalbible.activities.downloader.DownloadPrefs_ has no injectable members. Do you want to add an injectable constructor? required by class org.bspeice.minimalbible.activities.downloader.BookListFragment
Any clue on what's going on? It doesn't seem like the questions asking about "no injectable members" on other SO questions answered my case. I had a working app before adding the code above.
UPDATE: After doing some double-checking, I came across the following weird behavior. If I copy out the pre-built Android Annotations class, rename it, and inject that, everything works. Additionally, I can verify that the original built Android Annotations class (the DownloadPrefs_.java) does in fact exist in the .dex, so Dagger should have no reason to not be able to find it. Everything is doing a debug build, so I can't imagine ProGuard is messing anything up.
At this point, I'm going to create a minimal project to demonstrate the error, and file an issue with Dagger. In the mean time, just need to rewrite the Prefs class until I can get this sorted out.
UPDATE 5/12/2014
Here are the modules responsible for injection:
MinimalBibleModules.java
#Module(
injects = {
MinimalBible.class
},
includes = {
ActivityModules.class
}
)
public class MinimalBibleModules {
}
ActivityModules.java
#Module(
includes = {
ActivityDownloaderModule.class
}
)
public class ActivityModules {
}
ActivityDownloaderModule.java
#Module(
injects = {
BookListFragment.class,
DownloadManager.class,
BookRefreshTask.class
}
)
public class ActivityDownloaderModule {
#Provides #Singleton
DownloadManager provideDownloadManager() {
return new DownloadManager();
}
#Provides
EventBus provideBus() {
return new EventBus();
}
#Provides //#Singleton
DownloadPrefs_ provideDownloadPrefs() {
return new DownloadPrefs_(MinimalBible.getApplication());
}
}
Also, how the graph gets created:
MinimalBible.java
public class MinimalBible extends Application {
private ObjectGraph graph;
private static MinimalBible instance;
public MinimalBible() {
instance = this;
}
#Override
public void onCreate() {
graph = ObjectGraph.create(new MinimalBibleModules());
graph.inject(this);
}
There are two parts here. First, how you get access to the Context. You can do static things as you are, though that's not advisable. Generally, you should configure your graph with a stateful module that carries the context, like this:
#Module
class ApplicationModule {
private final Application application;
public ApplicationModule(Application app) {
this.application = app;
}
// you can mark this singleton, but it's minor overhead
// and the fact that you have a single instance stored
// means it's semantically equivalent. But for clarity
// it's sometimes good to make the point.
#Provides
#Singleton
Application application() {
return application;
}
// optionally: bind it as a Context with a qualifier.
// note: never bind Context without a qualifier annotation
// as Activity and Application are both Context subtypes.
#Provides
#Singleton
#PerApplication
Context appContext(Application app) {
// Doing this instead of returning this.application is
// semantically equivalent but links #PerApplication Context
// to Application, so in graph analysis and error reporting
// the link is clearer. That's a personal choice.
return app;
}
}
At any rate, you then when you create the graph:
Application appInstance = ...;
ObjectGraph appGraph = ObjectGraph.create(
MyAppModule.class,
new ApplicationModule(appInstance));
The Application is then seeded into the graph and can be depended-upon by other types that declare it as a dependency.

Categories

Resources