I try to use the new Dagger Android injection thing that works so far.
Now I want to extend it to my needs.
In my MainActivityModule I added a TestModule:
#Module
abstract class MainActivityModule {
#ActivityScope
#ContributesAndroidInjector(modules = arrayOf(TestModule::class))
internal abstract fun contributeMainActivityInjector(): MainActivity
}
The TestModule is really simple:
#Module
internal abstract class TestModule {
#Provides
internal fun provideTest(): String {
return "foo bar"
}
}
But I get this error: TestModule must be set
I looked into the generated source code but can't find a hint what I have to do. I searched for this at Google too but found only simple examples :-(
What have I forgotten? You can find the complete app at GitHub.
Edit
As Jeff Bowman sayed the provideTest() needs to be static. When I create a Java class like this:
#Module
public class TestModule {
#Provides
static String provide() {
return "foo bar";
}
}
it works.
So the final question: How to make this in Kotlin? This doesn't work:
#Module
internal abstract class TestModule {
companion object {
#Provides
#JvmStatic
internal fun provideTest(): String {
return "foo bar"
}
}
}
So I need another way to create a static method.
yeh I found a solution :-)
The Kotlin way to get a static method is to put the method in a companion object but now Dagger throws an error that the #Provides can only be used in a #Module. To fix this I annotated the companion object too
#Module
internal abstract class TestModule {
#Module
companion object {
#Provides
#JvmStatic
internal fun provideTest(): String {
return "foo bar"
}
}
}
An alternative solution, that's a bit cleaner in case you need to mock / replace dependencies in tests, would be to avoid making the class abstract, and keep the provides not static, like this:
#Module
internal class TestModule {
#Provides
internal fun provideTest(): String {
return "foo bar"
}
}
Related
I was trying to look for similar posts in SO before posting, but most of them talk about retrofit, and my question is about injecting a dependency (Service, Repository or whatever) into an object using #EntryPoint.
I have an object like this:
object FreddieMercuryYouAreTheOne {
lateinit var exception: ExceptionHandler
fun init(appContext: Context) {
setDependencies(appContext)
DoOtherInitStuff...
}
private fun setDependencies(appContext: Context){
val exh = EntryPointAccessors.fromApplication(appContext, Dependencies.ProvideExceptionHandler::class.java)
this.exception = exh.exceptionHandler()
}
/*
* THIS IS JUST AN ABSURD EXAMPLE
* */
private fun DoWhatever(cryptKey16CharStr: String, cryptInitializationVector16CharStr: String) {
try {
doWhatever
}catch(ex: Exception){
exception.logException(ex)
}
}
}
And then I have the class where I set the dependencies:
#Module
#InstallIn(SingletonComponent::class)
class Dependencies {
#EntryPoint
#InstallIn(SingletonComponent::class)
interface ProvideExceptionHandler {
fun exceptionHandler(): ExceptionHandler
}
}
And when building, what I get is the following error:
error: [Dagger/MissingBinding] exception.ExceptionHandler cannot be provided without an #Provides-annotated method.
Well, if I modify my dependencies module as follows:
#Module
#InstallIn(SingletonComponent::class)
class Dependencies {
#Provides
#Singleton
fun bindsExceptionHandler(): ExceptionHandler {
return ExceptionHandler
}
#EntryPoint
#InstallIn(SingletonComponent::class)
interface ProvideExceptionHandler {
fun exceptionHandler(): ExceptionHandler
}
}
Not only build, but it works, and ExceptionHandler is correctly injected in FreddieMercuryYouAreTheOne object, so, as you see, what I have is not exactly an issue, but wondering to know why I need two "providers" to be able to inject a dependency into an object, lets say, why is not enough with interface ProvideExceptionHandler (as Google documentation mentions).
I ask this because I have many class objects across my app, and most of them have dependencies, and so this way I'll have to create two providers for each dependency. Am I doing something wrong?
Entry Points used for field injection for un-supported classes by Hilt like a custom class or content provider.
in your case since you have object FreddieMercuryYouAreTheOne thats can't has a constructer . yeah you need :
1- to Provide the object(instance) you want
in your case:
#Provides
#Singleton
fun bindsExceptionHandler(): ExceptionHandler {
return ExceptionHandler
}
2- and then say hey!! ,i need field injection in my custom class
then you should use :
#EntryPoint
#InstallIn(SingletonComponent::class)
interface ProvideExceptionHandler {
fun exceptionHandler(): ExceptionHandler
}
if you have a normal class you just need to provide the object(just point #1). and then inject it in the constructer.
as i say #EntryPoint for un-supported classes field injection just.
Hint the recommended is constructer-injection over field injection
PLUS: ExceptionHandler and most of dependencies should be injected into ViewModel
I'm wondering if I understand SRP correctly using the Dagger under the hood as example.
#Binds processed code
For example, if I have
#Module
abstract class ModuleA {
#Binds
abstract fun provideStorage(storage: SharedStorage): Storage
}
In such a case annotation processor generates the follow code to initialize dagger graph:
private void initialize() {
this.sharedStorageProvider = SharedStorage_Factory.create();
}
#Provides processed code
And if I have (make concrete class and instance (non-static) function):
#Module
class StorageModule {
#Provides
fun provideStorage(): Storage {
return SharedStorage()
}
}
Annotation processor generates the follow code:
private void initialize(final StorageModule storageModuleParam) {
this.provideStorageProvider = StorageModule_ProvideStorageFactory.create(storageModuleParam);
}
Question:
Am I right if I say that #Binds annotation is designed in such a way that it follows the SRP principle more than #Provides?
I've found this post (last dot) about separation, but haven't found thoughts about SRP
I am developing an Android project with Kotlin and Dagger 2. I have a MyModule in which I defined some provider functions.
#Module
object MyModule {
#Provides
#JvmStatic
internal fun provideSomething(): Something {
...
}
}
In my Foo class, I inject Something as a member variable:
class Foo(#Inject val something: Something) {
}
But now I want to have this Foo class also be injectable to another class, e.g. in a class called Bar:
class Bar(#Inject val foo: Foo)
How to make that happen? If in Java I could do:
class Foo {
// I inject something
#Inject
private Something something;
// I also make Foo to be injectable to other class
#Inject
public Foo()
}
But how to achieve the same in my Kotlin Foo class?
Assuming Something is a class created by you. You don't even need a #Module to provide it.
You can do this,
class Something #Inject constructor() {
}
You just add #Inject to Something constructor and that's it, Dagger knows how to provide it. And then in your Foo class
class Foo #Inject constructor() {
#Inject
lateinit var something: Something
}
Done, no need to have #Module and #Component if you own Something class.
But
If the class Something is not under your control then we need to take the long route such as,
Step 1: Create Module
#Module
object MyModule {
#Provides
#JvmStatic
internal fun provideSomething(): Something {
...
}
}
Step 2: Define Component
#Component(modules = [MyModule::class])
interface MyComponent {
fun inject(foo: Foo) //mention the place where you need to inject variables
}
Step 3: Start Component
class Foo #Inject constructor(){
#Inject
lateinit var something:Something
init{
DaggerMyComponent().create().inject(this) //do this before using `something` or else face `UninitializedPropertyException`
//You can now freely use `something`, **Dagger** has already performed his magic
}
}
Update:
Let's say Something has a parameterized constructor and looks like this,
class Something #Inject constructor(mRandom : RandomClass), again two possibilities arise
If RandomClass is owned by you, you can just add #Inject to this RandomClass constructor like this,
class RandomClass #Inject constructor(){
}
that's it, Dagger will provide RandomClass wherever needed.
And If RandomClass is not under your control you need to provide it using a #Module like this,
#Module
object RandomModule {
#JvmStatic
#Provides
fun providesRandomClass(): RandomClass {
...
}
}
Add this #Module to your #Component and start the #Component wherever dependencies are needed(Example is already provided above in steps).
Moral of the story is, One way or another Dagger should know how to provide RandomClass
For your specific example, let's say we have
class Something #Inject constructor(mString:String,customType:CustomType){
}
Just tell Dagger how to provide that String and CustomType
#Module
object CustomModule {
#JvmStatic
#Provides
#Named("AnyName") //Just to differentiate which string we need
fun providesString() = "AnyName"
#JvmStatic
#Provides
fun providesCustomType(): CustomType {
...
}
}
and then this last little modification over Something constructor,
class Something #Inject constructor( #Named("AnyName") mString:String, customType:CustomType ){
}
In kotlin you have to do it like this:
class Foo #Inject constructor(val something: Something) {
}
I'm not so sure if you can name them the same either.
I have following project in Github : https://github.com/Ali-Rezaei/TMDb-Paging which I use Dagger2 for dependency injection.
One of my Module classes is as Follow in java :
#Module
public abstract class DetailModule {
#FragmentScoped
#ContributesAndroidInjector
abstract DetailFragment detailFragment();
#Provides
#ActivityScoped
static Movie provideMovie(DetailActivity activity) {
return activity.getIntent().getExtras().getParcelable(EXTRA_MOVIE);
}
}
As you can see provideMovie method is static. When I convert it to Kotlin :
#Module
abstract class DetailModule {
#FragmentScoped
#ContributesAndroidInjector
internal abstract fun detailFragment(): DetailFragment
companion object {
#Provides
#ActivityScoped
internal fun provideMovie(activity: DetailActivity): Movie {
return activity.intent.extras.getParcelable(EXTRA_MOVIE)
}
}
}
But when I build the project I get following Kotlin compiler error :
error: #Provides methods can only be present within a #Module or #ProducerModule
public final com.sample.android.tmdb.vo.Movie provideMovie$app_debug(#org.jetbrains.annotations.NotNull()
Could be any solution to have the class in Kotlin?
Comanion Object is technically different class and not annotated with #Module. (So you are getting that error)
you need to use JVM Annotations for methods. So Kotlin will create a static method inside DetailModule itself.
Try #JvmStatic
#Module
companion object {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideMovie(activity: DetailActivity): Movie {
return activity.intent.extras.getParcelable(EXTRA_MOVIE)
}
}
I'm using the new Dagger2 (ver 2.11) and I'm using the new features like AndroidInjector, and ContributesAndroidInjector. I have an activity subcomponent,
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules =
{UserListModule.class, MainFragmentModule.class})
#ActivityScope
abstract MainActivity bindsMainActivity();
}
#Module
public abstract class MainFragmentModule {
#ContributesAndroidInjector
#FragmentScope
#FragmentKey(UserListFragment.class)
abstract UserListFragment bindsUserListFragment();
}
And the UserListModule provides dependencies for the fragment. Some of the dependencies I just want to bind the instances , and return , like
#Binds
#ActivityScope
abstract UserListView mUserListView(UserListFragment userListFragment);
Instead of simply just return the dependency , like
#Provides
#ActivityScope
UserListView mUserListView(UserListFragment userListFragment){
return userListFragment;
}
My module contains some #Provides methods as well. Can we use both #Binds and #Provides methods in the same module? I tried as shown below
#Module
public abstract class UserListModule {
#Provides
#ActivityScope
UserListFragment mUserListFragment() {
return new UserListFragment();
}
#Binds
#ActivityScope
abstract UserListView mUserListView(UserListFragment userListFragment);
// other provides and binds methods...
......
.....
}
And it its throwing error
Error:(22, 8) error: dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
Is there any way to do this?
#Binds and #ContributesAndroidInjector methods must be abstract, because they don't have method bodies. That means that they must go on an interface or abstract class. #Provides methods may be static, which means they can go on abstract classes and Java-8-compiled interfaces, but non-static ("instance") #Provides methods don't work on abstract classes. This is explicitly listed in the Dagger FAQ, under the sections "Why can’t #Binds and instance #Provides methods go in the same module?" and "What do I do instead?".
If your #Provides method doesn't use instance state, you can mark it static, and it can go onto an abstract class adjacent to your #Binds methods. If not, consider putting the bindings like #Binds and #ContributesAndroidInjector into a separate class--possibly a static nested class--and including that using the includes attribute on Dagger's #Module annotation.
A little addition to Jeff's solution above:
you may create inner interface instead of static inner class, like this:
#Module(includes = AppModule.BindsModule.class)
public class AppModule {
// usual non-static #Provides
#Provides
#Singleton
Checkout provideCheckout(Billing billing, Products products) {
return Checkout.forApplication(billing, products);
}
// interface with #Binds
#Module
public interface BindsModule {
#Binds
ISettings bindSettings(Settings settings);
}
}
In kotlin, you can leverage companion object
#Module
abstract class MyDaggerModule {
#Binds
abstract fun provideSomething(somethingImpl: SomethingImpl): Something
companion object {
#Provides
fun provideAnotherThing(): AnotherThing {
return AnotherThing()
}
}
}
This is other type solution: Add modules to other module after that you can call top module in your component interface. It can be more efficiency because you can use abstract and static.
Details and examples are below:
For example, we have an component interface and two modules such as ComponentClasses, Module_ClassA and Module_ClassB.
Module_ClassA is:
#Module
public class Module_ClassA {
#Provides
static ClassA provideClassA(){
return new ClassA();
}
}
Module_ClassB is:
#Module
abstract class Module_ClassB {
#Binds
abstract ClassB bindClassB(Fragment fragment); //Example parameter
}
So now, we have two models. If you want use them together, you can add one of them to other. For example: You can add Module_ClassB to Module_ClassA:
#Module(includes = Module_ClassB.class)
public class Module_ClassA {
#Provides
static ClassA provideClassA(){
return new ClassA();
}
}
Finally, you do not need to add both modules to your component class. You can only add your top module on your component class, like that:
ComponentClasses is:
#Component(modules = Module_ClassA)
public interface ComponentClasses {
//Example code
ArrayList<CustomModel> getList();
}
However, you should be careful because you need to add your top module. Thus, Module_ClassA added on ComponentClasses interface.