Why would you need optional injection?
Hilt fragments need to be attached to Hilt activities and Hilt activities need to be attached to Hilt applications. While this is a natural restriction for pure Hilt codebases, it may be an issue during a migration to Hilt if you have a fragment or activity that is used in a non-Hilt context. For example, say you want to migrate a fragment to Hilt but it is used in too many places to migrate at once. Without optional injection, you would have to migrate every activity that uses that fragment to Hilt first otherwise the fragment will crash when looking for the Hilt components when it is trying to inject itself. Depending on the size of your codebase, this could be a large undertaking.
How to use @OptionalInject
If you mark an @AndroidEntryPoint
class with
@OptionalInject
then it will only try to inject if the parent is using Hilt and not require it.
Using this annotation will also cause a wasInjectedByHilt()
method to be
generated on the generated base class that returns true if it was successful
injecting.
Note: Because API generated on the base class is inaccessible to users of
the gradle plugin, there is an alternative
API to access this functionality using a static helper method in
OptionalInjectCheck
.
This gives you the chance to provide dependencies in a different way (usually whichever way you were getting dependencies before using Hilt).
For example:
@OptionalInject
@AndroidEntryPoint
public final class MyFragment extends Fragment {
@Inject Foo foo;
@Override public void onAttach(Activity activity) {
super.onAttach(activity); // Injection will happen here, but only if the Activity used Hilt
if (!OptionalInjectCheck.wasInjectedByHilt(this)) {
// Get Dagger components the previous way and inject
}
}
}
@OptionalInject
@AndroidEntryPoint
class MyFragment : Fragment() {
@Inject lateinit var foo: Foo
override fun onAttach(activity: Activity) {
super.onAttach(activity) // Injection will happen here, but only if the Activity used Hilt
if (!OptionalInjectCheck.wasInjectedByHilt(this)) {
// Get Dagger components the previous way and inject
}
}
}
Note that for activities, because Hilt injection is usually run as a part of
super.onCreate()
and it is recommended to do your own injection before
fragments are restored which also happens during super.onCreate()
, you likely
need to use an
OnContextAvailableListener
to run your non-Hilt injection code. Hilt uses the same listener under the hood,
so then the order would be Hilt’s OnContextAvailableListener
would run,
then yours, then fragments would be restored.