I\'m trying to convert a project that I\'m building to use the dagger-android API for the DI framework, but I\'m running into a dead end with an IllegalArgumentException whe
There could be other scenarious, where I got similar errors:
Possible case 1:
When you have a DialogFragment
shown from a Fragment
.
It's important to use the same FragmentManager.
For instance you have a "fragment-scoped screen":
@FragmentScope
@ContributesAndroidInjector(modules = [HomeInjectors::class])
abstract fun provideHomeFragment() HomeFragment
With a subcomponent
@Module
abstract class HomeInjectors {
@ChildFragmentScope
@ContributesAndroidInjector(modules = [DetailsModule::class])
abstract fun provideDetailsFragment(): DetailsDialogFragment
}
Important here to note, that when you show a dialog fragment, you should use child fragment manager not the Activity's one.
in this case, if you show dialog from the HomeFragment,
detailsDialog.show(activity.supportFragmentManager, "some tag)
and
detailsDialog.show(requireFragmentManager(), "some tag)
will not work.
You should do instead:
detailsDialog.show(childFragmentManager, "some tag)
Possible case 2: Parent fragment with child fragments.
In order to make child fragments with "smaller" scope (Sample code is the same as above, but consider DetailsDialogFragment a regular fragment and a child of the HomeFragment).
In my case, the child fragment wasn't able to find Parent's fragment injector.
The reason was that while providing a child fragment injector, I mistakenly made my BaseFragment implement HasFragmentInjector
.
However, since I use support fragments (AndroidX or whatever), I should have made BaseFragment implement HasSupportFragmentInjector
So the BaseFragment may look like:
import androidx.fragment.app.Fragment
abstract class BaseFragment : SometFragment(), HasSupportFragmentInjector {
@Inject lateinit var childFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return childFragmentInjector
}
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
}
It is useful when by certain reasons your "BaseFragment" must have other than DaggerFragment
parent
in my case, I did not define the Fragment
in @Module
class! so When I added it like :
@ActivityScope
@ContributesAndroidInjector(modules = {ActivityModule.class})
abstract InvestmentDetailsFragment bindInvestmentDetailsFragment();
my problem fixed!
When you inject using AndroidSupportInjection.inject(this)
from your HomeFragment
, Dagger will walk the parent-fragment hierarchy to find someone that implements HasSupportFragmentInjector
. To make it work, make your MainActivity
extends DaggerAppCompatActivity
which implements HasSupportFragmentInjector
.
From the doc of AndroidSupportInjection.inject(Fragment fragment)
:
Injects {@code fragment} if an associated {@link dagger.android.AndroidInjector} implementation can be found, otherwise throws an {@link IllegalArgumentException}.
Uses the following algorithm to find the appropriate {@code AndroidInjector} to use to inject {@code fragment}:
- Walks the parent-fragment hierarchy to find the a fragment that implements {@link HasSupportFragmentInjector}, and if none do
- Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements {@link HasSupportFragmentInjector}, and if not
- Uses the {@link android.app.Application} if it implements {@link HasSupportFragmentInjector}.
If none of them implement {@link HasSupportFragmentInjector}, a {@link IllegalArgumentException} is thrown.
@throws IllegalArgumentException if no parent fragment, activity, or application implements {@link HasSupportFragmentInjector}.
With this, Dagger will use
@FragmentScope
@ContributesAndroidInjector
abstract HomeFragment provideHomeFragment();
from your MainActivityModule
to inject inside your HomeFragment
.