Dagger Hilt-Dependency Injection and Implementation Principle of ViewModel

Insert picture description here

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.Factorybe created directly with the help of instead of the constructor. The entire injection process above does not customize any factory, just @ViewModelInjectone annotation is done, how is this achieved?

viewModels{…}

viewModels{...}It is an inherited Lazy<>ktx extension that can byeasily create a ViewModel through keywords. I Mvrxhave 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 get ActivityComponent,
  • 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 InstallInto 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 SavedStateViewModelFactorythe 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 @InstallInand @AndroidEntryPointthe 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

Guess you like

Origin blog.csdn.net/vitaviva/article/details/107124289