Roboguice - Instantiating an object from a constructor with arguments - android

Recently I downloaded Roboguice and gave it a try. In general I like it and I think it could ease some aspects in Android development process, but I encountered a situation which didn't find a solution yet: I want to inject a class, but that class has one, ore more constructors with several parameters.
In a such case, how would I specify which constructor to choose for instantiation, and pass the values to constructor?
For example I have the the class TestRobo with 2 constructors, and I want to instantiate the object from the second constructor, passing the firstName, lastName as parameters:
public class TestRobo implements ITestRobo {
public TestRobo(String fullName) {
//....
}
public TestRobo(String firstName, String lastName) {
//...
}
}
Right now, if I inject it like this:
#Inject
private ITestRobo testRobo;
It trows an exception that it couldn't find a suitable constructor.

Disclaimer 1: I am a newbie as well.
Disclaimer 2: Haven't tried this yet.
According to Google's own Guice Documentation, the way to specify which constructor to use is to add the #Inject decorator to it
class Test{
//This constructor is ignored
Test(){}
//This constructor is called during injection
#Inject
Test(Context pContext){}
}
In this case the injector calls the second constructor and tries to inject a Context object to call it. If it can't be found then it will throw an exception.
Apparently, you can also put the #Inject decorator to other methods, which will be called after the constructor is called during an injection.
Source:Official Google Guice Documentation

You need to bind ITestRobo to its TestRobo implementation. You can either do this by adding the #ProvidedBy(TestRobo.class) annotation to ITestRobo, or you can add a module and bind(ITestRobo.class).to(TestRobo.class) in your configure() method.

Related

Dagger 2 Inject Fields

I have a class that is dagger injected via the constructor. However I now need to add an argument to this constructor that is not provided via injection and is instead a run time argument.
In my previous job, we rolled our own DI so I am not up to speed with all the "magic" annotations that dagger offers yet. I figured it should be simple to add an argument to a constructor and still have dagger inject the remaining values (as it was very simple to do with the aforementioned "roll your own DI" solution I have implemented before).
However, it looks like this is not possible with dagger (i.e. assisted injection). So I have been reading on how to solve this issue but have become completely stumped.
Here is the class that I am currently (successfully) injecting ServiceA into:
class Foo #Inject constructor(private val serviceA: ServiceA) {
...
}
What I would like to do is add another argument to this constructor that will be provided at run time. In this case a simple flag to determine some behaviour in the class. And, given that Dagger doesn't support assisted injection, I need to remove injection from Foo and instead create a FooFactory to handle creation of Foo objects. This factory will have ServiceA injected into its constructor and will provide a method to construct an instance of Foo taking the boolean. I will then end up with a Foo class that looks like:
class Foo(private val serviceA: ServiceA, myNewArgument: Boolean) {
...
}
And a FooFactory that looks like:
#Singleton
class FooFactory #Inject constructor(private val serviceA: ServiceA) {
fun createFoo(myNewArgument: Boolean) {
return Foo(serviceA, myNewArgument)
}
}
And, although this is a complete mess to just get an extra constructor arg, it does the job.
The problem I am facing, is that my Foo class is actually an AndroidViewModel, and will need to be constructed through the ViewModelProvider.Factory contract, which has a create method which is invoked by the SDK to create the view model. You override this method to create an instance of the view model but the method has no parameters, so there is no way to propagate the flag into the view model through this method.
So the only way for me to get the flag propagated to the view models constructor is by having the factory itself take the flag as an argument to it's constructor, which, because dagger does not support assisted injection, is not possible.
So, instead I am planning to make dagger manually inject my dependencies into the FooFactory at initialization time. This is where I am stuck, I cannot figure out how on earth to get dagger to manually inject dependencies into my class.
My FooFactory now looks like:
class FooFactory(private val myNewArgument: Boolean) : ViewModelProvider.Factory {
init {
// I need to trigger injection here... how though???
}
#Inject
lateinit var serviceA: ServiceA
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return Foo(
serviceA,
myNewArgument
) as T
}
}
So, somehow in the init block of the factory, I need to ask dagger to inject the fields annotated with #Inject. But I have no idea how to do this, I have tried following the answers at the following questions and tutorials:
https://proandroiddev.com/from-dagger-components-to-manual-dependency-injection-110015abe6e0
Dagger 2 - injecting non Android classes
Dagger 2 injection in non Activity Java class
None of these seem to work for my use case and I'm starting to lose it. Could anyone point me in the right direction? Is this dagger framework massively over engineered/complicated for not much benefit (this is the conclusion I am coming to at this point, all I want to do is achieve DI for testing purposes, I don't want to have to write factories so I can add an extra argument to a constructor)...

How to inject using interface in dagger2

Considering the following structure:
public class WaterWorld implements IWorld {
...
#Inject
CreationMode creationMode;
#Override
public final void init() {
WorldModule.getComponent().inject(this);
}
...
}
Is it possible for WaterWorld to get dependencies if the Component has following structure:
void inject(IWorld world);
I am getting null this way. However, if I try to do it in the following
void inject(WaterWorld world);
then it works. However, I have many classes implementing IWorld. I wanted to do it this way: void inject(IWorld world); How to do this or is there some other generic solution?
No, Dagger will always inject the class specified in the inject(Foo foo) method. It will inject objects in the parent types, but it will not inject objects in subclasses.
Dagger uses the type specified to generate code for the injection, but if you don't specify the specific class then Dagger simply doesn't know about it and won't inject its fields.
Note: You don't give any specific example, but it looks like you could be very well using constructor injection instead, which has no need for manual injection or inject methods in your component. If you have multiple implementations of the same interface you might even take a look at multi bindings with Dagger.

Using Mockito, How do I spy Kotlin class with val interface field declaration?

I was trying to test retrofit's api client class in Kotlin using Mockito in Android, which looks like this:
class SomeApiClient : SomeApi {
private val service: SomeApiService
constructor(service: SomeApiService) {
this.service = service
}
}
it implements SomeApi interface and contains one retrofit service interface as constructor parameter.
When spying classes with Mockito, the classes need to have no-arg constructors otherwise your tests will fail with MockitoException:
org.mockito.exceptions.base.MockitoException: Unable to initialize #Spy annotated field 'SomeClass'.
Please ensure that the type 'SomeClass' has a no-arg constructor.
If classes have constructors with all parameters containing default values, then Kotlin generates parameterless constructors for it. However, what if the class receives interface as constructor parameter (and has corresponding field with val declaration), then how can we apply default value to it or declare empty constructor?
Thanks!
When spying classes with Mockito, the classes need to have no-arg constructors
No, that's only if you don't provide the instance yourself. So provide the instance, e.g.
#Spy val client = SomeApiClient(Mockito.mock(SomeApiService::class.java))

How to use #Qualifers for multiple instances of same type with Dagger 2? [duplicate]

This is a Canonical Question because this is a common error with Dagger 2.
If your question was flagged as a duplicate please read this post carefully and make sure to understand what this error means and why it occured. If this post does not work for you make sure to include where and how you provide the mentioned classes and include the full error message in your question like the one here.
I tried to use a dependency with Dagger 2, but I receive the following error when I try to compile my project:
error: com.example.MyDependency cannot be provided without an #Inject constructor or from an #Provides-annotated method.
com.example.MyDependency is provided at
com.example.MyComponent.myDependency()
What does this mean and how can I fix it?
I have a component and tried to provide a dependency. My basic setup looks like this:
// this is the dependency I try to use
class MyDependency {}
#Component
interface MyComponent {
// I want to make it accessible to be used with my component
MyDependency myDependency();
}
tl;dr You forgot to either add an #Inject to your constructor so that Dagger can use Constructor Injection to provide the object, or you need some method in one of your Modules that creates or binds the object.
What's going on?
Have a good look at the error message: It states that you try to request a dependency but Dagger has no way to provide or create it. It simply does not know how to, because it cannot be provided without an #Inject constructor or from an #Provides-annotated method.
A close look at the error message shows the class (a) that you are trying to provide and the component (b) that needs it.
com.example.MyDependency (a) is provided at
com.example.MyComponent.myDependency() (b)
You have to make sure that (b) can create or provide (a) to fix your issue.
It looks a bit more complex if you tried to inject your dependency somewhere else, but you can still see the full stack of events—in this case a constructor injection missing a dependency. The class (a) that you are trying to provide and the location (b) where Dagger tried injecting it. It also tells you where that dependent class was created (c) and again the component (d) that failed providing (a).
com.example.MyDependency cannot be provided without an #Inject constructor or from an #Provides-annotated method.
com.example.MyDependency (a) is injected at
com.example.DependentClass.(dependency) (b)
com.example.DependentClass is provided at (c)
com.example.MyComponent.myDependency() (d)
The same applies here: Make sure that (d) knows how to provide (a) and you're good to go.
How do I fix this?
Have a look at the error as shown above. Make sure you understand where it occured and what you are trying to inject. Then tell Dagger how to provide your object.
an #Inject constructor
As the error states, you try to use MyDependency but MyComponent does not know how to do that. If we have a look at the example it becomes clear why:
class MyDependency {}
The class has no #Inject annotated constructor! And there is no other module in the component, so there is nothing Dagger could do.
If you want to use constructor injection you can just add an #Inject annotated constructor and are done. Dagger will see this constructor and know how to create your class.
class MyDependency {
#Inject
MyDependency() { /**/ }
}
That is all you have to do when you can make use of constructor injection.
from an #Provides-annotated method
The error message states a second option, which allows you to provide an object if you don't want—or can't—use constructor injection. You can also add a #Provides annotated method to a module and add this module to your component.
#Module
class MyModule {
#Provides
MyDependency provideMyDependency() {
return new MyDependency();
}
}
#Component(modules = MyModule.class)
interface MyComponent {
MyDependency myDependency();
}
This way Dagger can use your module to create and provide your dependency. It is a little bit more boilerplate than using Constructor Injection, but you will have to use Modules for everything that needs further setup or that does not have an annotated constructor, e.g. third party libraries like Retrofit, OkHttp, or Gson.
There are also other ways to provide a dependency from a component. A #SubComponent has access to its parents dependencies, and a component dependency can expose some of its dependencies to its dependent components. But at some point everything Dagger provides needs to either have an #Inject constructor or a Module providing it.
But I did add MyDependency!
Pay close attention to the details. You probably are using an interface when you are only providing the implementation, or try to use a parent class when Dagger only knows about the subclass.
Maybe you added a custom #Qualifier or used #Named("typeA") with it. To Dagger this is a completely different object! Double check that you actually provide and request the same dependency.
Read the error and make sure that you either have an #Inject annotated constructor, a module that has a #Provides method that provides that type, or a parent component that does.
What if I want to provide an implementation for my interface?
A simple example like the following shows how one class extends another:
class MyDependency extends MyBaseDependency {
#Inject MyDependency() { super(); }
}
This will inform Dagger about MyDependency, but not about MyBaseDependency.
If you have one class implementing an interface or extending a super class you have to declare that. If you provide MyDependency this does not mean that Dagger can provide MyBaseDependency. You can use #Binds to tell Dagger about your implementation and provide it when the super class is required.
#Module
interface MyModule {
#Binds
MyBaseDependency provideMyBaseDependency(MyDependency implementation);
}

Using Dagger for dependency injection on constructors

So, I'm currently redesigning an Android app of mine to use Dagger. My app is large and complicated, and I recently came across the following scenario:
Object A requires a special DebugLogger instance which is a perfect candidate for injection. Instead of passing around the logger I can just inject it through A's constructor. This looks something like this:
class A
{
private DebugLogger logger;
#Inject
public A(DebugLogger logger)
{
this.logger = logger;
}
// Additional methods of A follow, etc.
}
So far this makes sense. However, A needs to be constructed by another class B. Multiple instances of A must be constructed, so following Dagger's way of doing things, I simple inject a Provider<A> into B:
class B
{
private Provider<A> aFactory;
#Inject
public B(Provider<A> aFactory)
{
this.aFactory = aFactory;
}
}
Ok, good so far. But wait, suddenly A needs additional inputs, such as an integer called "amount" that is vital to its construction. Now, my constructor for A needs to look like this:
#Inject
public A(DebugLogger logger, int amount)
{
...
}
Suddenly this new parameter interferes with injection. Moreover, even if this did work, there would be no way for me to pass in "amount" when retrieving a new instance from the provider, unless I am mistaken. There's several things I could do here, and my question is which one is the best?
I could refactor A by adding a setAmount() method that is expected to be called after the constructor. This is ugly, however, because it forces me to delay construction of A until "amount" has been filled in. If I had two such parameters, "amount" and "frequency", then I would have two setters, which would mean either complicated checking to ensure that construction of A resumes after both setters are called, or I would have to add yet a third method into the mix, like so:
(Somewhere in B):
A inst = aFactory.get();
inst.setAmount(5);
inst.setFrequency(7);
inst.doConstructionThatRequiresAmountAndFrequency();
The other alternative is that I don't use constructor-based injection and go with field-based injection. But now, I have to make my fields public. This doesn't sit well with me, because now I am obligated to reveal internal data of my classes to other classes.
So far, the only somewhat elegant solution I can think of is to use field-based injection for providers, like so:
class A
{
#Inject
public Provider<DebugLogger> loggerProvider;
private DebugLogger logger;
public A(int amount, int frequency)
{
logger = loggerProvider.get();
// Do fancy things with amount and frequency here
...
}
}
Even still, I'm unsure about the timing, since I'm not sure if Dagger will inject the provider before my constructor is called.
Is there a better way? Am I just missing something about how Dagger works?
What you are talking about is known as assisted injection and is not currently supported by Dagger in any automatic fashion.
You can work around this with the factory pattern:
class AFactory {
#Inject DebugLogger debuggLogger;
public A create(int amount, int frequency) {
return new A(debuggLogger, amount);
}
}
Now you can inject this factory and use it to create instances of A:
class B {
#Inject AFactory aFactory;
//...
}
and when you need to create an A with your 'amount' and 'frequency' you use the factory.
A a = aFactory.create(amount, frequency);
This allows for A to have final instances of the logger, amount, and frequency fields while still using injection to provide the logger instance.
Guice has an assisted injection plugin which essentially automates the creation of these factories for you. There have been discussion on the Dagger mailing list about the appropriate way for them to be added but nothing has been decided upon as of this writing.
What Jake's post says is perfectly true. That said, we (some of the Google folk who work with Guice and Dagger) are working on an alternative version of "assisted injection" or automatic-factory generation which should be usable by Guice or Dagger or stand-alone - that is, it will generate factory class source code for you. These factory classes will (if appropriate) be injectable as any standard JSR-330 class would. But it is not yet released.
Pending a solution like this, Jake Wharton's approach is advisable.
You're having a problem because you are mixing injectables and non injectables in your constructor. The general rules for injection that will save you tons of heartache and keep your code clean are:
Injectables can ask for other injectables in their constructor, but not for newables.
Newables can ask for other newables in their constructor but not for injectables.
Injectables are service type objects, ie objects that do work such as a CreditCardProcessor, MusicPlayer, etc.
Newables are value type objects such as CreditCard, Song, etc.
Jake's post is great, but there is more simple way. Google created AutoFactory library for creating factory automatically at compile time.
First, create class A with #AutoFactory annotation and #Provided annotation for injecting arguments:
#AutoFactory
public class A {
private DebugLogger logger;
public A(#Provided DebugLogger logger, int amount, int frequency) {
this.logger = logger;
}
}
Then library creates AFactory class at compile time. So you need just inject the factory to a constructor of B class.
public class B {
private final AFactory aFactory;
#Inject
public B(AFactory aFactory) {
this.aFactory = aFactory;
}
public A createA(int amount, int frequency) {
return aFactory.create(amount, frequency);
}
}
I just want to add that years passed after this question has been posted and now there is a library called
AssistedInject which has been created by Jake and friends at Square,
to solve the exact same problem and is fully compatible with Dagger 2.
You can find it here: https://github.com/square/AssistedInject
Dagger 2 now has support for assisted injection which should help solve your usecase.
https://dagger.dev/dev-guide/assisted-injection
You can wrap your class like this:
#AssistedInject
public A(DebugLogger logger, #Assisted int amount)
{
...
}
Create a factory for this class.
#AssistedFactory
public interface MyDataFactory {
A create(int amount);
}
and in your client, you can use:
#Inject MyDataFactory dataFactory;
void setupA(int amount) {
A a = dataFactory.create(config);
// ...
}

Categories

Resources