Dagger.android UserScope - android

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.

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.

Subcomponent (unscoped) may not reference scoped bindings: #Singleton #Provides #org.jetbrains.annotations.NotNull

I am using dagger 2.11
Module
#Module
class MyModule {
#Provides
fun provideString() : String = "yo"
#Provides #Named("injector")
fun provideInzectorString() : String = "named_injection"
#Singleton #Provides //The error goes away if I remove #Singleton
fun provideRepository() = Repository(Interceptor(),"")
}
Activity binding module
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [MyModule::class])
abstract fun suggestionActivity() : SuggestionsActivity
#ContributesAndroidInjector(modules = [MyModule::class])
abstract fun editSubscriptionActivity() : SubscribeActivity
}
AppComponent
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
MyModule.class
})
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(MyApplication application);
AppComponent build();
}
void inject(MyApplication app);
}
I get this error on compilation
SubscribeActivitySubcomponent (unscoped) may not reference scoped bindings:
#Singleton #Provides #org.jetbrains.annotations.NotNull
I have seen these solutions 1 and 2. Both ask you to annotate your appcomponent with #Singleton which I am already doing. What is wrong with my code?
The problem is MyModule's scope(Application or singleton) is bigger than an activity scope.
#ContributesAndroidInjector(modules = [MyModule::class])
abstract fun suggestionActivity() : SuggestionsActivity
Remove the two (modules = [MyModule::class])s or define activity specific modules.
You don't need MyModule here. It's redundant because it's already included in the AppComponent.

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

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

Dagger2 dependent components

In my app I have a component with Application scope (same as Singleton) that provides a ViewModel Factory, and a dependent component with Activity scope that injects the factory in a fragment.
The application component is defined as follows:
#Component(modules = [AppModule::class, /* other stuff */, ViewModelModule::class])
#ApplicationScope
interface AppComponent {
fun inject(app: Application)
/* other stuff */
val viewModelFactory: ViewModelFactory
}
The view model module is defined as follows:
#ApplicationScope
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(DisplayEntryViewModelImpl::class)
abstract fun bindDisplayEntryViewModel(displayEntryViewModelImpl: DisplayEntryViewModelImpl): ViewModel
}
The activity scope component is defined as follows:
#Component(dependencies = [AppComponent::class], modules = [DisplayEntryActivityModule::class])
#ActivityScope
interface DisplayEntryActivityComponent {
fun inject(displayEntryActivity: DisplayEntryActivity)
fun inject(displayEntryFragment: DisplayEntryFragment)
}
When I try to inject the viewmodel factory in the fragment I get this error:
error: android.arch.lifecycle.ViewModelProvider.Factory cannot be provided without an #Provides- or #Produces-annotated method.
If I update the activity component to include the view model module, like this
#Component(dependencies = [AppComponent::class], modules = [DisplayEntryActivityModule::class, ViewModelModule::class])
#ActivityScope
interface DisplayEntryActivityComponent {
fun inject(displayEntryActivity: DisplayEntryActivity)
fun inject(displayEntryFragment: DisplayEntryFragment)
}
Then it compiles. My understanding is that dependent components have access to the injected members from the parent component if the parent component explicitly provides those members, as I do here with the
val viewModelFactory: ViewModelFactory
so why do I still need to provide the viewmodel module in the activity scope component?
When using dependencies, dagger will use that component to inject member.
Parent component must explicitly declare objects which can be used in child components.
#Component(modules = [AppModule::class, /* other stuff */, ViewModelModule::class])
#ApplicationScope
interface AppComponent {
fun inject(app: Application)
fun viewModelFactory(): ViewModelProvider.Factory
fun viewModel(): ViewModel
}
You can take a look at this article:
https://proandroiddev.com/dagger-2-part-ii-custom-scopes-component-dependencies-subcomponents-697c1fa1cfc

Categories

Resources