"dagger-android" library, how to provide Context(Application)? - android

It is confusing that all web search results seem to use slightly different versions of Dagger or different approaches. I followed the example which claims that is the better "new way". (https://proandroiddev.com/exploring-the-new-dagger-android-module-9eb6075f1a46) The full sample source code is here (https://github.com/jshvarts/DaggerAndroidKotlinSampleApp).
Now, I want to know how a non-activity/fragment class could be provided with a Context. So, I added a simple class like this,
class Sample2 #Inject constructor (var app: Application)
{
fun print()
{
Log.d("sample2", app.packageName);
}
}
But even though the sample project had AppModule and AppComponent, the compilation failed, because app could not be provided.
I have searched the web and found this method (https://github.com/google/dagger/issues/832). I followed that code and modified the sample's AppModule and AppComponent like this,
#Module(includes = [AndroidInjectionModule::class])
abstract class AppModule {
#Binds
abstract fun application(app: App):Application;
#Singleton
#Provides
fun providesCommonHelloService() = CommonHelloService()
}
#Singleton
#Component(modules = [AndroidInjectionModule::class,
AppModule::class, ActivitiesModule::class])
interface AppComponent:AndroidInjector<App>
{
#Component.Builder
abstract class Builder:AndroidInjector.Builder<App>(){}
}
class App : Application(), HasActivityInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate()
{
super.onCreate()
DaggerAppComponent.builder().create(this).inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}
But the, I get the following compilation error.
AppModule.java:7: error: A #Module may not contain both non-static #Provides methods and abstract #Binds or #Multibinds declarations
public abstract class AppModule {
Again, as I have said in the beginning, the Dagger examples on the Internet are all slightly different, I do not know how to take two features from two examples.

It is better to separate #Binds and #Provides, you can create a component class:
#Singleton
#Component(
modules = [(AppModule::class)]
)
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
then an AppModule class for all your #Provides
#Module
class AppModule() {
#Singleton
#Provides
fun providesCommonHelloService() = CommonHelloService()
}

Related

Dagger 2 Unable to provide and inject Interface and its implementation in android

#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

Dagger2: Field injection in ViewModel which has compile time and run time dependencies

I have a list of Students which is displayed in RecyclerView and for each Student, I have StudentViewModel. As the StudentViewModel needs Student object which is retrieved runtime from database, I am not sure how can I setup constructor injection for StudentViewModel so I didn't attempt DI here. However, now I need another dependency in the viewmodel which can be resolved compile time and which is my NavUtil class.
I managed to get it injected in my StudentViewModel with following lines:
DaggerAppComponent.builder()
.application(MyApp.INSTANCE)
.build()
.inject(this)
But I don't think this is the correct way as I am referring Application class from my ViewModel just for the sake of DI. I read it might be possible with ViewModelFactory, but I am not able to find any solid example or reference to this. I am already using it for the ViewModels which has compile-time dependencies though, but not able to figure out the way for ViewModel(s) which has compile time and rumtime dependencies. Would be great if someone could point me in right direction.
BTW here's my overall implementation for DI:
App Component
#Component(modules = [AndroidInjectionModule::class, AndroidSupportInjectionModule::class,
AppModule::class, AppProvider::class,StudentProvider::class])
#Singleton
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MyApp)
fun inject(studentViewModel: StudentViewModel)
StudentProvider
#Module
abstract class StudentProvider {
#ContributesAndroidInjector(modules = [(ViewModelModule::class)])
abstract fun bindHomeActivity(): HomeActivity
}
ViewModelModules
#Module
class ViewModelModule {
#ActivityContext
#Provides
fun provideNavUtils(activity: Activity): NavUtil = NavUtil(activity)
}
StudentViewModel
class StudentViewModel(student: Student) : BaseViewModel() {
#Inject
lateinit var navUtils: NavUtil
init {
DaggerAppComponent.builder()
.application(MyApp.INSTANCE)
.build()
.inject(this)
}
fun onSettingsTapped() {
navUtils.navigateToSettings()
}
}
NavUtil
class NavUtil #Inject constructor(#ActivityContext private val context: Context) {
fun navigateToSettings() {
context.startActivity(Intent(context, SettingsActivity::class.java))
}
}
EDIT: It looks like activity context is not getting inject and instead, Application context is inject.

Android dagger cannot be provided without an Provides-annotated method

I decided to learn dagger dependency injection framework. After some tutorials I try to implement dagger into my project. However I got this error
com\assigment\di\component\AppComponent.java:11: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> cannot be provided without an #Provides-annotated method.
public abstract void inject(#org.jetbrains.annotations.NotNull()
^
java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> is injected at
dagger.android.DispatchingAndroidInjector.<init>(injectorFactories)
dagger.android.DispatchingAndroidInjector<android.app.Activity> is injected at
assigment.com.assigment.App.activityInjector
assigment.com.assigment.App is injected at
assigment.com.assigment.di.component.AppComponent.inject(assigment.com.assigment.App)
I try to solve this for 2 days but with no luck. So here's how I set up my project
class App : Application(), HasActivityInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent
.builder()
.appModule(AppModule())
.build()
}
override fun activityInjector(): AndroidInjector<Activity> {
return activityInjector
}
}
Here's my app component
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
and here's my appModule class
AppModule
#Module
class AppModule {
private val url = "http://test.lt/v1/"
#Provides
#Singleton
fun provideApplication(app: Application): Context = app
}
So what I'm missing with this implementation?
Add AndroidInjectionModule.class and ActivityBuilder.class to your AppComponent
#Singleton
#Component(modules = [AndroidInjectionModule::class, AppModule::class, ActivityBuilder::class ])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
You have two problems.
To get rid of the compiler error add AndroidInjectionModule::class to Component modules:
#Singleton
#Component(modules = [AndroidInjectionModule::class, AppModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
With #Component.Builder annotated interface you define the builder interface with an annotated #BindsInstance method application() (note that there is not an appModule method declared in the Builder).
With such declaration you can build your component using application(this):
appComponent = DaggerAppComponent
.builder()
.application(this)
.build()
In this way this application instance is bound inside the component.
Just as a side note: Binding Instances are documented here, but personally I found the explanation quite hard to grasp for someone learning dagger, like me.
Detail answer with explanation
Component - >
Component is a graph. Component will provide injected instances by using modules.
#Component(
modules = [
AndroidInjectionModule::class, //We didn’t create this. It is an internal class in Dagger 2.10. Provides our activities and fragments with given module
ActivityModule::class,
]
)
#Singleton
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
fun addContext(#BindsInstance context: Context): Builder
fun build(): AppComponent
}
}
We created ActivityModule module. This is a given module to dagger. We map all our activities here. And Dagger know our activities in compile time. In our app we have MainActivity. So we map it here.
#Module
abstract class ActivityModule {
#ContributesAndroidInjector
public abstract MainActivity bindMainActivity();
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<application
android:name=".App"
App.kt
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().addContext(this).build()
}
}
And Make sure to extend your activity with DaggerAppCompatActivity that will auto inject before onCreate

Dagger.android UserScope

I'm trying to create UserScope with https://google.github.io/dagger/android I have #Singleton, #ActivityScope, #FragmentScope and #UserScope.
AppComponent
#Singleton
#Component(
modules = [
AndroidSupportInjectionModule::class,
ActivityBindingModule::class,
AppModule::class,
ApiModule::class
]
)
interface AppComponent {
fun inject(application: Application)
fun createUserComponent(): UserComponent.Builder
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun apiModule(module: ApiModule): Builder
fun build(): AppComponent
}
}
UserComponent:
#UserScope
#Subcomponent(
modules = [UserModule::class]
)
interface UserComponent {
#Subcomponent.Builder
interface Builder {
#BindsInstance
fun user(user: User): Builder
fun build(): UserComponent
}
}
UserModule:
#Module
class UserModule {
#UserScope
#Provides
fun provideUser(user: User): User = user
}
Here is how I am creating UserComponent after successfull login:
private fun createSession(user: User) {
userComponent = appComponent.createUserComponent().user(user).build()
}
And also I have UserManager which is triggering an issue with User injection constructor param
#UserScope
class SomeManager #Inject constructor(
private val apiService: ApiService,
private val user: User
) {}
Error message: Error:[dagger.android.AndroidInjector.inject(T)]
domain.model.authorize.User cannot be provided without an #Inject
constructor or from an #Provides-annotated method.
di.modules.MainActivityBindingModule_ContributeUserFragment.UserFragmentSubcomponent
scoped with #di.scopes.FragmentScope may not reference bindings with
different scopes: #di.scopes.UserScope class
domain.managers.SomeManager
I want to create #UserScope to manage related ApiManagers lifecycle
UPD
#Module
class UserFragmentModule {
#Provides
#FragmentScope
fun provideViewModelFactory(someModule: SomeModule) = UserFragmentViewModelFactory(someModule)
}
ContributesAndroidInjector logic:
#Module
interface ActivityBindingModule {
#ActivityScope
#ContributesAndroidInjector(modules = [SplashModule::class])
fun contributeSplashActivity(): SplashActivity
#ActivityScope
#ContributesAndroidInjector(modules = [SignInModule::class])
fun contributeAuthorizeActivity(): Activity
#ActivityScope
#ContributesAndroidInjector(modules = [MainModule::class])
fun contributeMainActivity(): MainActivity
}
#Module(includes = [MainActivityBindingModule::class])
class MainModule
#Module
interface MainActivityBindingModule {
#FragmentScope
#ContributesAndroidInjector(modules = [UserFragmentModule::class])
fun contributeUserFragment(): UserFragment
}
You are trying to inject a User instance that is provided only in the #UserScope in #FragmentScope.
Basically, the rule is whenever you need a User injected - you need to be in the #UserScope.
You need to change your #FragmentScope to #UserScope annotation in all places like that.
From what you posted I believe it is here:
#Module
class UserFragmentModule {
#Provides
#UserScope
fun provideViewModelFactory(someModule: SomeModule) = UserFragmentViewModelFactory(someModule)
}
And you'll need to move your UserFragmentModule to the UserComponent:
#UserScope
#Subcomponent(
modules = [UserModule::class, UserFragmentModule::class]
)
interface UserComponent {
And you'll also need to inject with application.userComponent into your UserFragment
Here is a good article about subcomponents. I'd recommend you to read it to gain full understanding.
Another option, I'm not sure about this one, but maybe you can just inject your UserFragment with your application.userComponent.
I think your problem is wrong use of scopes.
Scopes help us to handle injected item life-cycle to prevent keep un-needed object in whole app life cycle.
and take look at this image:
Your modules should be in same scope that your component is.
for example in your AppComponent, you have ActivityBindingModule that have ActivityScope. why you do this?
I think it is better to have a activityComopnent that have dependency toappComponent. If your activityComponent had some dependency in upper scoped component (appComponent), first you had add dependency of that component. (dependencies= [ AppComponent::class]). Second, you had to expose needed dependency in appComponent with a method that return needed dependency object. Finally in wireUpping you should call appComponent as call activity modules in Dagger wire upping. ( I will show this in a example)
So all you need is activityComponent like this:
#ActivityScope
#Component(modules = [ActivityBindingModule::class],dependencies= [ AppComponent::class])
interface activityComponent {
.
.
.
This is my example written in java that shows how connect appComponent and activityComponent:
#Singleton
#Component(modules = {ApplicationModule.class ,ServicesModule.class})
public interface ApplicationComponent {
void inject(ImicoApplication imicoApplication);
// exposed for child-components.
Context getContext();
Application getApplication();
CompanyService getCompanyService();
CategoryService getCategoryService();
RatingService getRatingService();
NewsService getNewsService();
AuthorizationManager getAuthorizationManager();
}
And this is activityComponent:
#ActivityScope
#Component(dependencies = {ApplicationComponent.class},modules = {SearchActivityModule.class})
public interface SearchserviceComponent {
void inject (SearchFragment searchFragment);
}
And in SearchFragment do this for wire-up:
DaggerSearchserviceComponent.builder()
.applicationComponent(((ImicoApplication) getActivity().getApplication()).getApplicationComponent())
.searchActivityModule(new SearchActivityModule(this)).build().inject(this);
If in SearchFragment i need CompanyService just inject it and it is provided and exposed by applicationComponent.

Dagger 2 not injecting sharedPreference

Hi i am new to dagger 2 and trying to inject an instance of sharedPreference inside my MyActivity class below:
class MyApplication : Application() {
companion object {
#JvmStatic lateinit var applicationComponent : ApplicationComponent
}
override fun onCreate() {
super.onCreate()
applicationComponent = DaggerApplicationComponent.builder().androidModule(AndroidModule(this)).build()
}
}
Here is the component and modules
#Singleton
#Component(modules = arrayOf(AndroidModule::class))
interface ApplicationComponent {
fun inject(mainActivity: MainActivity)
}
#Module
class AndroidModule (private val application: Application){
#Provides
#Singleton
fun provideApplicationContext() : Context = application
#Provides
#Singleton
fun provideSharedPreference() : SharedPreferences = application.getSharedPreferences("shared pref", Context.MODE_PRIVATE)
}
class MainActivity: Activity{
#Inject
internal lateinit var sharedPreference: SharedPreferences
#Inject
internal lateinit var MainScreenPresenter: MainScreenContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_Screen)
MyApplication.applicationComponent.inject(this)
sharedPreference.toString()
initiateViews()
}
}
I get the error below:
Error:(7, 1) error: android.content.SharedPreferences cannot be provided without an #Provides- or #Produces-annotated method.
You have done it a little bit incorrect. First of all now there is dagger-android that helps with the problem of principle that solves the problem that components (such as Activities) should not know about how the injection happens.
you can read it here: https://medium.com/#iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe
Just to be sure that dagger dependencies are in the Android project:
android {
kapt {
generateStubs = true
}
}
compile "com.google.dagger:dagger:2.13"
compile "com.google.dagger:dagger-android:2.13"
compile "com.google.dagger:dagger-android-support:2.13"
kapt "com.google.dagger:dagger-compiler:2.13"
kapt "com.google.dagger:dagger-android-processor:2.13"
Your mistake is that you didn't tell to your graph that you want to make injections into the MainActivity. In the best way you should create Subcomponent for MainActivity, connect it with another Module for MainActivity that have injections that you want to inject into the MainActivity, set in your AppComponent the connection with Subcomponent and only than in MainAcitivy onCreate() method inject your dependency graph. But with dagger-android everything is easier.
Here is the code:
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBindingModule::class,
AppModule::class
])
interface AppComponent : AndroidInjector<DaggerApplication> {
fun inject(application: MyApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance fun application(application: MyApplication): Builder
fun build(): AppComponent
}
}
AndroidSupportInjectionModule.class : This goes from the dagger.android.support library. And it provides Android components (Activities/Fragments/Services/BroadcastReceiver/ContentProvider) with our module.
#Component.Builder in dagger2.10 provides us better way to create a builder of DaggerAppComponent.
#BindsInstance in the Builder will automatically create an instance of MyApplication so in AppModule we don't need to instantiate with MyApplication. It is already a dependency in the graph.
ActivityBindingModule.clas is our. I will tell about it later.
You can read more about this part here: https://proandroiddev.com/dagger-2-component-builder-1f2b91237856
Next is AppModule.class :
#Module
abstract class AppModule{
#Binds
abstract fun provideContext(application: MyApplication) : Context
#Module
companion object {
#JvmStatic
#Provides
fun provideSharedPreferences(context: Context): SharedPreferences =
context.getSharedPreferences("SharedPreferences", Context.MODE_PRIVATE)
}
}
#Binds annotation replaces #Provides annotation and it just returns the value in the function parameter. As you see an instance of MyApplication is already in the graph and there is no need to inject MyApplication in the AppModule constructor.
NOTE: function with #Binds annotation should be abstract, and if there are function with #Provides annotation they should be static.
The solution in Kotlin for static funcitons you can find here: https://github.com/google/dagger/issues/900
ActivityBindingModule.class:
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun bindMainActivity(): MainActivity
}
With the ActivityBindingModule class we just create separate Module that will create Subcomponents for Android components for us.
More about ContributesAndroidInjector and Binds you can read here:
https://proandroiddev.com/dagger-2-annotations-binds-contributesandroidinjector-a09e6a57758f
MainActivityModule.class:
#Module
abstract class MainActivityModule {
#Binds
internal abstract fun provideMainActivity(activity: MainActivity): MainActivity
}
MyApplication.class:
class MyApplication: DaggerApplication(){
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
val appComponent = DaggerAppComponent.builder()
.application(this)
.build()
appComponent.inject(this)
return appComponent
}
}
Do not forget insert Application in the Mainfest file.
<application
android:name=".MyApplication"
...
>
...
</application>
For your Application class you need to implement DaggerApplication that implements HasActivityInjector/HasFragmentInjector/etc as well as call AndroidInjection.inject().
About this you can read more here : https://google.github.io/dagger/android.html
MainActivity.class:
class MainActivity : DaggerAppCompatActivity() {
#Inject
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("TAAAAG", sharedPreferences.toString())
}
}
As you can see MainActivity now does not know how SharedPreferences are injected. Actually there is AndroidInjection.inject(this); in the DaggerAppCompatActivity. If you don't extend you class from this, than you need to specify it in onCreate method by yourself, otherwise no injections will be done.
EDIT:
You can check the code from GitHub: https://github.com/Belka1000867/Dagger2Kotlin

Categories

Resources