I've multiple modules in my app, each one of them have his own Module for UI Injection.
Now, i want to have "feed" fragment that have a some pieces from other modules.
So i trying doing it using FragmentContainerView.
I want to Inject the fragment that defined on other module. I'm trying to "include" my other fragment, so i can navigate into this fragment, but if i'm try to inject it, i'm getting this following error:
error: [Dagger/MissingBinding] DotsFragment cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
Any idea how can i do it?
HomeModule:
#Module(
includes = [
DotsModule::class
]
)
abstract class HomeModule {
}
DotsModule:
#Module
abstract class DotsModule {
#FragmentScoped
#ContributesAndroidInjector
abstract fun contributeDotsFragment(): DotsFragment
}
HomeFragment:
#Inject
lateinit var dotsFragment: DotsFragment
Just push the Fragment into the container as described in the docs, and don't worry about the injection. You don't even need the explicit inclusion of DotsModule from HomeModule, though you may choose to keep it, especially if HomeModule is in a different Activity. DotsFragment will inject itself in onAttach.
Unlike most classes that use Dagger injection, Fragments can't be injected in their constructors: Android will only call the zero-arg or one-arg Fragment() constructor, so there isn't room for other constructor parameters. When you see your abstract fun contributeDotsFragment() function with #ContributesAndroidInjector, it is not the case that DotsFragment is available on your graph to be constructed or provided. Instead, Dagger does some magic1 that allows the Fragment to have its #Inject-labeled fields populated2 once you call AndroidSupportInjection.inject(this) in onAttach, which might happen in DaggerFragment if you inherit from that. Your Fragment has to assume that anything marked #Inject is null until the Fragment is attached.
Consequently, if you need an instance of DotsFragment, you can just call DotsFragment() or delegate to your FragmentManager's FragmentFactory (which will likely just call DotsFragment()). You can also use overloads to FragmentTransaction.replace(...) that take a Class<Fragment> rather than a Fragment instance.
The Fragment will inject itself later in onAttach, as it would if Android itself called the constructor. If you interact with the Fragment before that, be careful, as its #Inject-annotated fields will not be populated yet. If you need to interact with the Fragment in ways that use Dagger-provided instances, it may be best to create a Bundle that the Fragment can use to initialize itself in onCreate.
1: Behind the scenes, dagger.android creates a code-generated subcomponent instance, probably called DotsModule_BindDotsFragment.DotsFragmentSubcomponent, and registers that subcomponent as an AndroidInjector.Factory<DotsFragment> in a Map. The generated subcomponent receives all the modules you set on the #ContributesAndroidInjector annotation as well as all the scope annotations you set on the abstract fun. In onAttach (yours or DaggerFragment's), the call to AndroidSupportInjection.inject(this) reads from that Map, finds the Injector/Subcomponent, creates an instance, and uses that to inject the Fragment with its own set of #FragmentScope instances.
2 The #Inject-annotated methods will be called too. That's method injection, compared to field injection; the general term is members injection for all of the post-constructor initialization Dagger can do.
In the doc, you can see this graph:
So there is this "ActivityRetainedScope" for activity ViewModel, but what about fragment ViewModel? What is the right scope to be used in this case? And the same question for the component, I don't see a component for the fragment ViewModel?
Is it mandatory to add #AndroidEntryPoint annotation on all dependent classes like fragment dependent upon activity. Is there any alternative solution for overcome this exception?
Just add #AndroidEntryPoint to your parent Activity class:
And yes, it's a mandatory process if you want to use Hilt. You could use Dagger to get away with this.
Given the case : ProfileActivity and EditProfileActivity both extend from a BaseActivity, should each activity have their own viewmodel (ProfileViewModel and EditProfileViewModel), use the same viewmodel in the two activities or inherit the model from the parent but obverse it in each activity?
Thanks
It depends on the scope of your ViewModels. If you want your activities to communicate with each other, then you can create a BaseViewModel scoped to your BaseActivity and use that to do IPC among ProfileActivity and EditProfileActivity, given that they expend BaseActivity.
You can also create three viewModels, one for each. Use the BaseViewModel for IPC and the ProfileViewModel and editProfileViewModel for delegation, where the activities will be light themselves and will delegate everything down to their viewModels. This can be achieved through the combination of LiveData observers and DataBinding.
Currently I have a fragment class with a viewmodel being used in multiple activities. The viewmodel(viewModelOne) was added to be used in Activity1 but when Activity2 uses the fragment there is a runtime exception since the fragment's viewmodel is scoped to be used in the activity(eg. ViewModelProviders.of(getActivity()) and Activity2 doesn't instantiate viewModelOne. The easy solution is to create the viewModelOne instance in the Activity2 class but i am not a fan of this solution since viewModelOne is not relevant for this activity class. Any other solutions or recommendations?