Dagger 2.10 subcomponent generator - Injector validation fails - android

I'm trying to create an annotation processor which will process my MVP views (fragments) to auto-generated Subcomponents (similar to https://github.com/lukaspili/Auto-Dagger2, but for the new Dagger 2.10 android injectors)
So far, I've been able to generate appropriate files, but there is a strange error message when compiling generated components
Error:(22, 58) error: #dagger.android.support.FragmentKey methods should bind dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>, not dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>. See google.github.io/dagger/android
The structure of Factory module and Subcomponent files should be correct, because as soon as I copy-paste the generated classes and create a regular classes (both Factory module and Subcomponent) and use real classes instead of generated ones, the message is no longer shown and compilation succeeds
It seems like the problem lies in AndroidMapKeyValidator (link), where !MoreTypes.equivalence().equivalent(returnType, intendedReturnType) call apparently fails, but I don't have much of an experience debugging annotation processors, so I don't know why precisely...
Can maybe anyone help where to search for the problem?
Thanks
FYI: MyFragment does extend android.support.v4.app.Fragment
My files:
Generated Factory
#Module
public interface BuildersModule {
#Binds
#IntoMap
#FragmentKey(MyFragment.class)
abstract AndroidInjector.Factory<? extends Fragment> factory(MySubcomponent.Builder builder);
}
Generated subcomponent
#Subcomponent(modules = MyModule.class)
public interface MySubcomponent extends AndroidInjector<MyFragment> {
MyPresenter presenter();
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MyFragment> {}
}

If anyone is interested in solution:
I found out that for some reason, references of ClassType-s compared during project's compile time are not the same when validating generated method.
And these references, despite the fact that they are pointing to the same class, are checked for equality in auto-common library in EqualVisitor.visitDeclared method. Apparently, this can be a bug in auto-common, because Elements in visitDeclared are compared by object reference, but not type reference.
So workaround here is to use local fixed copy of auto-common library and exclude all dependencies of the original library.
//TODO think if this is the correct solution to cast both elements
//return aElement.equals(bElement)
return ((TypeElement) aElement).getQualifiedName().equals(((TypeElement) bElement).getQualifiedName())
&& equal(a.getEnclosingType(), b.getEnclosingType(), newVisiting)
&& equalLists(a.getTypeArguments(), b.getTypeArguments(), newVisiting);
I still have to check why those references are not the same, and I have to think how the equality check can be fixed properly in auto-common (I use just a quickfix) before filing an issue in auto-common repo.

Related

Dagger 2 - Strategies for reducing number of classes which require annotations

So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.

using Dagger2 to provide a type with params got error "cannot be provided without an #Provides-annotated method" mean [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);
}

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);
}

Kotlin annotation processing ignores items with similar names

I recently converted the majority of my project to kotlin. Now I encounter several unusual errors that all seem to relate to annotation libraries. Needless to say, it didn't happen in Java.
I'll describe the cases - one in Dagger and one in Butterknife.
1.
When having 2 #Provides methods in different models with the same name.
For example in file "FooProvider.kt" having a "provideFooOrBar" method
#Module
class FooProvider(private val view: FooActivity) {
...
#Provides #FooScope fun provideView() = view
#Provides #FooScope fun provideFooOrBar() = Foo()
}
And having another file "BarProvider.kt" with the same method name
#Module
class BarProvider(private val view: BarActivity) {
...
#Provides #BarScope fun provideView() = view
#Provides #BarScope fun provideFooOrBar() = Bar()
}
In this case, Dagger fails to generate some factory libraries and I get the following compilation error:
Error:(27, 32) error: cannot find symbol class FooProvider_ProvideFooOrBarFactory
A sample project reproducing the issue can be found at https://github.com/maxandron/DaggerIssue325
2.
This is an issue when using Butterknife. When having two #Bind annotated variables in two different classes - One of them just fails to initialize at runtime without any compilation error!
For example if I have:
class FooActivity {
#Bind(R.id.foo) lateinit var mFoo: View
}
class NotFooActivity {
#Bind(R.id.not_foo) lateinit var mFoo: View
}
Then one of them (or both?) will just fail to initialize without any error. Causing a kotlin.UninitializedPropertyAccessException: lateinit property mFoo has not been initialized exception to be thrown when the field is accessed.
Is it something I'm doing wrong in configuring Kotlin or is it a kotlin bug?
Thank you in advance!
Ron
I was having this issue, so I started to investigate and it's caused because Kapt is only checking the method name when comparing them, and they are added in a set, thus duplicates are not allowed. The same happens for annotated fields, so currently you can have one method/field name per annotation.
I added the class name to the equals method and the annotations were properly handled now, but the tests broke and I don't know how they work, so I hope someone knows how to fix this.
It turned out to be a bug with kapt.
I posted an issue on Kotlin's bug tracker and the issue is now marked as fixed.
This solution was merged
Should be resolved in Kotlin version 1.0.2
So to somewhat answer the kotlin.UninitializedPropertyAccessException: lateinit issue, I was running into the exact same thing in my project. What I did which "solved the issue" for me was to remove Butterknife from the offending class, in this case it was just a viewHolder for my new expandable RecyclerView, and then run the app again.
Running the app, after switching all my #Bind(R.id.my_view_id) to the "old school" findViewById(R.id.my_view_id) as MyViewType worked, but then subsequently afterwards I switched the same class back to Butterknife and the UninitializedPropertyAccessException went away, and it seems like it won't come back unless something in the class changes, then you'll have to repeat this process again.
My suspicion here is that this has something to do with Kotlin not supporting incremental compilation, and somehow by changing the auto-generated code it was forced to recompile. But I could be way off here, just thought I'd share my experience.

Dagger can't find injectable members

I've been trying to setup activity graphs for my Android application with Dagger (v1.1.0). Although it compiles fine, I get this error shown below (full trace here):
No injectable members on com.f2prateek.couchpotato.ui.ActivityActionBarController. Do you want to add an injectable constructor? required by class com.f2prateek.couchpotato.ui.fragments.DetailedMovieGridFragment
I'll do my best to highlight the important sections, but in case I miss something, my full project is on GitHub. Just run ./gradlew clean assemble to build the apk.
My ActivityModule has the provider method that Dagger can't seem to find.
#Provides #Singleton ActivityActionBarController provideActionBarTitleController() {
return new ActivityActionBarController(activity);
}
This module is definitely being added to the applicationGraph (which then is saved as the activityGraph) in my BaseActivity, and the BaseFragment is injecting itself into the activityGraph.
Dagger uses an annotation processor to write it's code. If your class is not annotated, it can't find it. Make sure you add annotations to every class. E.g. if you want to inject a member, you must use #Inject. Try to make sure all classes you're injecting is in listed under #Module(inject=...). Try to ensure all classes are listed in the module class somewhere.

Categories

Resources