Dagger Hilt
Hilt is the latest DI library released by Google, which reduces the cost of using Dagger in Android and supports the injection of various common Android components, including our commonly used ViewModel.
Dagger Hilt-Dependency Injection Framework officially recommended by Android introduces the injection method of ViewModel, which is very simple to use:
class ActivityViewModel @ViewModelInject constructor(
private val repository: Repository,
@Assisted private val savedState: SavedStateHandle
) : ViewModel() {
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val viewModel by viewModels<ActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
ViewModle creation needs to ViewModel.Factory
be created directly with the help of instead of the constructor. The entire injection process above does not customize any factory, just @ViewModelInject
one annotation is done, how is this achieved?
viewModels{…}
viewModels{...}
It is an inherited Lazy<>
ktx extension that can by
easily create a ViewModel through keywords. I Mvrx
have seen similar usage in airbnb before, and it seems to be borrowed by Jetpack.
The official is not just simply borrowing (chao) Jian (xi), it has its own secrets
@MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
// ComponentActivity#getDefaultViewModelProviderFactory()
defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, {
viewModelStore }, factoryPromise)
}
Knowing from the source code, ViewModel.Factory is used by default defaultViewModelProviderFactory
. So as long as you rewrite this defaultViewModelProviderFactory, you can hook the custom implementation into it
Override getDefaultViewModelProviderFactory
From the previous article, we know that Hilt will modify the parent class of the injected object at compile time.
@AndroidEntryPoint
class MainActivity : AppCompatActivity(R.layout.activity_main) {
MainActivity originally inherited from AppCompatActivity
, but after Hilt processing, the parent class becomesHilt_MainActivity
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManager<Object> {
private volatile ActivityComponentManager componentManager;
...
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
ViewModelProvider.Factory factory = DefaultViewModelFactories.getActivityFactory(this);
if (factory != null) {
return factory;
}
return super.getDefaultViewModelProviderFactory();
}
}
HIlt_MainActivity DefaultViewModelFactories.getActivityFactory(this)
returns ViewModel.Factory through .
public final class DefaultViewModelFactories {
@Nullable
public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
return getFactoryFromSet(
EntryPoints.get(activity, ActivityEntryPoint.class).getActivityViewModelFactory());
}
//...
}
One sentence summary: Hilt obtains ViewModelFactory through Component generated by Dagger
EntryPoints.get(activity, ActivityEntryPoint.class)
Create and getActivityComponent
,getActivityViewModelFactory()
Get Factory through Module in Component
Create ActivityComponent
public final class EntryPoints {
@Nonnull
public static <T> T get(Object component, Class<T> entryPoint) {
if (component instanceof GeneratedComponent) {
return entryPoint.cast(component);
} else if (component instanceof GeneratedComponentManager) {
return entryPoint.cast(((GeneratedComponentManager<?>) component).generatedComponent());
}
}
}
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManager<Object> {
private volatile ActivityComponentManager componentManager;
...
@Override
public final Object generatedComponent() {
return componentManager().generatedComponent();
}
protected ActivityComponentManager createComponentManager() {
return new ActivityComponentManager(this);
}
protected final ActivityComponentManager componentManager() {
if (componentManager == null) {
synchronized (componentManagerLock) {
if (componentManager == null) {
componentManager = createComponentManager();
}
}
}
return componentManager;
}
As above, Hilt_MainActivity inherits from GeneratedComponentManager
, ActivityComponentManager gets generatedComponent
public class ActivityComponentManager implements GeneratedComponentManager<Object> {
//...
protected Object createComponent() {
return ((ActivityComponentBuilderEntryPoint)
activityRetainedComponentManager.generatedComponent())
.activityComponentBuilder()
.activity(activity)
.build();
}
}
Eventually ActivityComponentManager
, the ActivityComponent is created through the acquisition of Hilt_MainActivity , and the activityRetainedComponentManager (with the help of ViewModel) in the ActivityComponentManager ensures the reuse of the ActivityComponent.
Get ViewModelFactory
After getting the ActivityComponent, get the ViewModelFactory through the internal Model.
Hilt is provided ActivityModule
, you can InstallIn
to ActivityComponent
. The Provider of Factory is defined in ActivityModule
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public final class ViewModelFactoryModules {
/**
* Hilt Modules for providing the activity level ViewModelFactory
*/
@Module
@InstallIn(ActivityComponent.class)
public abstract static class ActivityModule {
@NonNull
@Multibinds
abstract Map<String, ViewModelAssistedFactory<? extends ViewModel>> viewModelFactoriesMap();
@Provides
@IntoSet
@NonNull
@DefaultActivityViewModelFactory
static ViewModelProvider.Factory provideFactory(
@NonNull Activity activity,
@NonNull Application application,
@NonNull Map<String, Provider<ViewModelAssistedFactory<? extends ViewModel>>>
viewModelFactories) {
// Hilt guarantees concrete activity is a subclass of ComponentActivity.
SavedStateRegistryOwner owner = (ComponentActivity) activity;
Bundle defaultArgs = activity.getIntent() != null
? activity.getIntent().getExtras() : null;
SavedStateViewModelFactory delegate =
new SavedStateViewModelFactory(application, owner, defaultArgs);
return new HiltViewModelFactory(owner, defaultArgs, delegate, viewModelFactories)
By SavedStateViewModelFactory
the defaultArgs
, the desired parameters are implanted ViewModel
to sum up
Hilt rewrites the parent class of Activity or Fragment at compile time and obtains the custom ViewModel.Factory method, thus hooking the creation process of ViewModle and injecting ViewModel. The whole implementation process is based on @InstallIn
and @AndroidEntryPoint
the annotations, which in itself is a Hilt best practice, it is worth learning and learning from
Refer to the previous article
Dagger Hilt-Android officially recommended dependency injection framework