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)
}
}
Related
I have these Repositories dependent on DataSources.
class LocationRepository: Repository<String>(LocationDataSource())
class ItemRepository: Repository<String>(ItemDataSource())
I would like to inject the Repository class with Hilt like this to prevent code duplication.
abstract class Repository<T> {
#Inject lateinit var dataSource: DataSource<T>
...
}
I have tried this, but am not sure how to get Hilt to use the right ones.
#Qualifier
#Retention(AnnotationRetention.BINARY)
annotation class ItemDataSourceAnnotation
#Qualifier
#Retention(AnnotationRetention.BINARY)
annotation class LocationDataSourceAnnotation
#Module
#InstallIn(SingletonComponent::class)
object DataSourceModule {
#ItemDataSourceAnnotation
#Provides
#Singleton
fun provideItemDataSource(): DataSource{
return ItemDataSource()
}
#LocationDataSourceAnnotation
#Provides
#Singleton
fun provideLocationDataSource(): DataSource{
return LocationDataSource()
}
}
#Module
#InstallIn(SingletonComponent::class)
object RepositoryModule {
#Provides
#Singleton
fun providesItemRepository(
#ItemDataSourceAnnotation itemDataSource: ItemDataSource
): ItemRepository {
return ItemRepository()
}
#Provides
#Singleton
fun providesLocationRepository(
#LocationDataSourceAnnotation locationDataSource: LocationDataSource
): LocationRepository {
return LocationRepository()
}
}
If what you want is to avoid having the data source field in each repository subclass, you could add a data source type parameter and val to Repository:
class Repository<ValueT, DataSourceT>(val dataSource: DataSourceT) {
...
}
class LocationRepository: Repository<Location, LocationDataSource> #Inject constructor(dataSource: LocationDataSource): super(dataSource) {
...
}
It's better to use constructor injection than field injection, and using constructor injection, you have to pass the constructor parameters through the subclasses -- you can't do it only in the base class.
#Module
abstract class PersonUsecaseModule{
#Provides
internal fun provideUseCase(useCase: GetPersonUseCaseImpl): PersonUseCase = useCase
#Provides
internal fun provideMutableLiveData() = MutableLiveData<PersonUseCase.Result>()
#Provides
internal fun providePersonWidgetImplScreen(widget: PersonWidgetImpl): PersonWidget = widget
}
this is my module class and i am injecting it in MainActivity i am getting error
error: com.anil.gorestapp.person.injection.PersonUsecaseModule is abstract and has instance #Provides methods. Consider making the methods static or including a non-abstract subclass of the module instead.
public abstract interface ApplicationComponent {
I don't know why i am getting this Error Please help me what i am doing mistake
lateinit var personWidget: PersonWidget
AppLication component :
#Singleton
#Component(
modules = [
ApplicationModule::class,
ActivityModule::class,
NetworkModule::class
]
)
interface ApplicationComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(application: MainApplication)
}
ActivityModule
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindSharedPreferences(appPreferenceImpl: AppPreferenceImpl): AppPreference
}
person module
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindSharedPreferences(appPreferenceImpl: AppPreferenceImpl): AppPreference
}
So the issue is, You have declared your module as "abstract" and along with this, you are also using #Provides annotation on methods which are returning implementation of interface.
Dagger doesn't allow that. You can fix this issue in two way:
First Way: Remove abstract from your module like this:
#Module
class ActivityModule {
#Provides
fun providePersonWidget(personWidget: PersonWidgetImpl) : PersonWidget = personWidget
}
Second way: Use #Bind annotation on method instead of provide like this
#Module
abstract class ActivityModule {
#Binds
abstract fun providePersonWidget(personWidget: PersonWidgetImpl) : PersonWidget
}
Note: In second way you can declare your method and class as abstract but cannot return anything.
If you are still not very clear with my answer, you can refer this branch which I have created for you.
https://github.com/parmeshtoyou/StackOverflow/compare/sof_23_oct_21_dagger_issue?expand=1
While doing a dagger-2 to hilt migration, got this error for a module
Error:
FooModule.Companion is listed as a module, but it is a companion
object class.
Add #Module to the enclosing class
and reference that instead.
Before Hilt
#Module
abstract class FooModule {
#Binds
#FooScope
abstract fun bindsManager(impl: FooManagerImpl): FooManager
#Module
companion object {
#Provides
#FooScope
#JvmStatic
fun providesConfig(prefs: SharedPreferences): FooConfig = FooConfigImpl(prefs)
}
}
After Hilt
#InstallIn(ApplicationComponent::class)
#Module
abstract class FooModule {
#Binds
#FooScope
abstract fun bindsManager(impl: FooManagerImpl): FooManager
#InstallIn(ApplicationComponent::class)
#Module
companion object {
#Provides
#FooScope
#JvmStatic
fun providesConfig(prefs: SharedPreferences): FooConfig = FooConfigImpl(prefs)
}
}
Migration doc reference: https://developer.android.com/codelabs/android-dagger-to-hilt#4
Dagger 2.26 made it an error to include a companion object module in the modules parameter of #Component or #Subcomponent. Instead, companion objects are automatically included if the containing class is a module. Hilt's #InstallIn simply adds the annotated module to a generated component class, so you get the same error if you annotate a companion object with #InstallIn.
Remove #InstallIn (and #Module) from the companion object, and everything should work fine.
You need to change companion object to object
#InstallIn(ApplicationComponent::class)
#Module
abstract class FooModule {
#Binds
#FooScope
abstract fun bindsManager(impl: FooManagerImpl): FooManager
#InstallIn(ApplicationComponent::class)
#Module
object AppModule{
#Provides
#FooScope
fun providesConfig(prefs: SharedPreferences): FooConfig = FooConfigImpl(prefs)
}
}
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.
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"
}
}