Dagger Hilt - ViewModel的依赖注入及实现原理

简介: Dagger Hilt VIewModel 依赖注入的原理

在这里插入图片描述

Dagger Hilt

Hilt是谷歌最新发布的DI库,降低了Android中Dagger的使用成本,支持各种常见Android组件的注入,也包括我们常用的ViewModel。

Dagger Hilt - Android官方推荐的依赖注入框架
一文中介绍了ViewModel的注入方法,使用起来非常简单:

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创建需要借助ViewModel.Factory、而非构造函数直接创建。上面整个注入过程并没有自定义任何factory,仅仅@ViewModelInject一个注解就搞定了,这是怎么实现的呢?

viewModels{...}

viewModels{...}是一个继承Lazy<>的ktx扩展,可以通过by关键字方便地创建ViewModel,以前我在airbnb的Mvrx中见到过类似用法,看来是被Jetpack借鉴了。

官方自然不只是简单的借(chao)鉴(xi),里面自有其玄机

@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)
}

通过源码知道,ViewModel.Factory默认使用了defaultViewModelProviderFactory。所以只要重写这个defaultViewModelProviderFactory,就可以hook自定义实现进去

重写getDefaultViewModelProviderFactory

通过前一篇文章我们知道,Hilt会在编译期修改被注入对象的父类,

@AndroidEntryPoint
class MainActivity : AppCompatActivity(R.layout.activity_main) {

MainActivity原本继承自AppCompatActivity,但经Hilt处理后父类变为了Hilt_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)返回ViewModel.Factory。

public final class DefaultViewModelFactories {

  @Nullable
  public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
    return getFactoryFromSet(
        EntryPoints.get(activity, ActivityEntryPoint.class).getActivityViewModelFactory());
  }
  //...
}

一句话概括:Hilt通过Dagger生成的Component获取ViewModelFactory

  • EntryPoints.get(activity, ActivityEntryPoint.class)创建并获取ActivityComponent
  • getActivityViewModelFactory()通过Component内的Module获取Factory

创建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;
  }

如上,Hilt_MainActivity继承自GeneratedComponentManager,ActivityComponentManager获取generatedComponent

public class ActivityComponentManager implements GeneratedComponentManager<Object> {

 //...

  protected Object createComponent() {
    return ((ActivityComponentBuilderEntryPoint)
            activityRetainedComponentManager.generatedComponent())
        .activityComponentBuilder()
        .activity(activity)
        .build();
  }
}

最终通过Hilt_MainActivity的ActivityComponentManager获创建ActivityComponent,ActivityComponentManager内部通过activityRetainedComponentManager(借助ViewModel)保证了ActivityComponent的复用。

获取ViewModelFactory

获取ActivityComponent后,通过内部的Model获取ViewModelFactory。
Hilt中提供了ActivityModule,可以InstallInActivityComponent。ActivityModule中定义了Factory的Provider

@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)

通过SavedStateViewModelFactorydefaultArgs,将ViewModel所需的参数进行注入

总结

Hilt在编译期改写Activity或者Fragment的父类,获取了自定义的ViewModel.Factory的方法,从而hook了ViewModle的创建过程,对ViewModel进行注入。整个实现过程借助@InstallIn以及@AndroidEntryPoint的注解,本身就是一个Hilt的最佳实践,值得学习和借鉴

参考上一篇 Dagger Hilt - Android官方推荐的依赖注入框架

目录
相关文章
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
2112 0
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
|
前端开发 JavaScript 流计算
React 18 流式渲染:解锁极致性能优化实践
React 18 流式渲染:解锁极致性能优化实践
749 80
|
9月前
|
Java 关系型数据库 MySQL
基于springboot的电脑商城系统
本研究聚焦3C数码电商系统的技术升级,针对传统架构性能瓶颈与用户体验不足问题,基于SpringBoot微服务框架构建高并发、易扩展的新型电商平台,结合MySQL、B/S架构与Java技术,提升系统稳定性与智能化水平。
|
人工智能 自然语言处理 程序员
通义灵码2.0全新升级,AI程序员全面开放使用
通义灵码2.0来了,成为全球首个同时上线JetBrains和VSCode的AI 程序员产品!立即下载更新最新插件使用。
4617 85
通义灵码2.0全新升级,AI程序员全面开放使用
|
机器学习/深度学习 计算机视觉
RT-DETR改进策略【注意力机制篇】| EMA 即插即用模块,提高远距离建模依赖(含二次创新)
RT-DETR改进策略【注意力机制篇】| EMA 即插即用模块,提高远距离建模依赖(含二次创新)
762 1
RT-DETR改进策略【注意力机制篇】| EMA 即插即用模块,提高远距离建模依赖(含二次创新)
|
数据采集 文字识别 JavaScript
视觉分析开发范例:Puppeteer截图+计算机视觉动态定位
本文介绍了在现代互联网中,传统DOM爬虫难以应对动态加载和视觉驱动内容的问题,并提出了“视觉爬虫”的解决方案。通过Puppeteer实现浏览器自动化,结合计算机视觉技术完成页面元素的动态定位与信息提取。文章对比了DOM爬虫与视觉爬虫的技术特点,展示了基于Node.js的核心代码示例,用于小红书平台的视频搜索、播放及截图处理。最后指出,视觉爬虫能够突破传统限制,在强JS渲染和动态内容场景中更具优势,为数据采集提供了新方向。
883 1
视觉分析开发范例:Puppeteer截图+计算机视觉动态定位
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
1146 18
|
人工智能 数据可视化 API
自动查文献+写代码+跑数据+出报告!港大开源 Auto Deep Research 搞定科研全流程
Auto-Deep-Research 是一款由香港大学开源的个人 AI 助理,基于模块化多 Agent 架构,专注于深度研究任务,兼容多种大语言模型,并提供一键启动和文件解析等强大功能。
1778 4
自动查文献+写代码+跑数据+出报告!港大开源 Auto Deep Research 搞定科研全流程
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
1420 1
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
1295 12