• Andorid Jetpack Hilt


    前言

    本blog 是学习Hilt官方文档后,随手记的笔记,欢迎一起探讨交流Hilt用法

    Hilt 用到的注解和类

    注解解释
    @HiltAndroidApp@HiltAndroidApp 会触发 Hilt 的代码生成操作,生成的代码包括应用的一个基类,该基类充当应用级依赖项容器
    @EntryPoint“hilt管理代码与非hilt管理代码之间的边界它是代码首次进入 Hilt 所管理对象的图的位置”
    @InstallIn指定要在其中安装入口点的组件
    @AndroidEntryPoint充当组件持有者
    EntryPointAccessors访问入口点工具
    @ApplicationContext预定义限定符
    @ActivityContext预定义限定符
    DFM (动态功能模块)
    @Inject提供依赖的对象
    @WorkerInjectWorkManager注入
    @AssistedWorkManager注入
    @Singleton限定作用域
    Hilt 模块 @Module +@InstallIn 注释

    解决问题

    1.不能通过构造函数注入接口
    2.不能通过构造函数注入不归您所有的类型(外部库的类)

    解决思路

    @Module注释的Hilt模块类里面的@Binds注释的函数会告知 Hilt 如何提供某些类型的实例

    示例

    @Module
    @InstallIn(ActivityComponent::class)  依赖项目注入所有拓展自ActivityComponent的子类
    
    abstract class AnalyticsModule {
    
      @Binds
      abstract fun bindAnalyticsService(
        analyticsServiceImpl: AnalyticsServiceImpl
      ): AnalyticsService
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    说明:在 Hilt 模块内创建一个带有 @Binds 注释的抽象函数(@Binds 注释会告知 Hilt 在需要提供接口的实例时要使用哪种实现),

    带有@Binds的函数会向 Hilt 提供以下信息:函数返回类型会告知 Hilt 函数提供哪个接口的实例。
    函数参数会告知 Hilt 要提供哪种实现。

    使用 @Provides 注入实例

    解决问题
    1.Retrofit、OkHttpClient、Room或者必须使用构建器模式创建实例,也同样无法使用构造器函数注入

    解决思路

    @Provides注释函数会向 Hilt 提供以下信息

    1. 函数返回类型会告知 Hilt 函数提供哪个类型的实例。
    2. 函数参数会告知 Hilt 相应类型的依赖项。
    3. 函数主体会告知 Hilt 如何提供相应类型的实例。每当需要提供该类型的实例时,Hilt 都会执行函数主体。

    示例

    @Module
    @InstallIn(ActivityComponent::class)
    object AnalyticsModule {
    
      @Provides
      fun provideAnalyticsService(
        // Potential dependencies of this type
      ): AnalyticsService {
          return Retrofit.Builder()
                   .baseUrl("https://example.com")
                   .build()
                   .create(AnalyticsService::class.java)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    为同一类型提供多个绑定

    解决问题

    1.以依赖项的形式提供同一类型的不同实现

    解决思路

    您可以使用限定符为同一类型定义多个绑定

    限定符是一种注释,当为某个类型定义了多个绑定时,您可以使用它来标识该类型的特定绑定。

    示例步骤

    1.定义要由于为@Binds 或 @Provides 方法添加注释的限定符

    @Qualifier
    @Retention(AnnotationRetention.BINARY)
    annotation class AuthInterceptorOkHttpClient
    
    @Qualifier
    @Retention(AnnotationRetention.BINARY)
    annotation class OtherInterceptorOkHttpClient
    
    
    @Module
    @InstallIn(ApplicationComponent::class)
    object NetworkModule {
    
      @AuthInterceptorOkHttpClient
      @Provides
      fun provideAuthInterceptorOkHttpClient(
        authInterceptor: AuthInterceptor
      ): OkHttpClient {
          return OkHttpClient.Builder()
                   .addInterceptor(authInterceptor)
                   .build()
      }
    
      @OtherInterceptorOkHttpClient
      @Provides
      fun provideOtherInterceptorOkHttpClient(
        otherInterceptor: OtherInterceptor
      ): OkHttpClient {
          return OkHttpClient.Builder()
                   .addInterceptor(otherInterceptor)
                   .build()
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    调用示例

    系统类中,自定义类中,hiltModule中

    // As a dependency of another class.
    @Module
    @InstallIn(ActivityComponent::class)
    object AnalyticsModule {
    
      @Provides
      fun provideAnalyticsService(
        @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
      ): AnalyticsService {
          return Retrofit.Builder()
                   .baseUrl("https://example.com")
                   .client(okHttpClient)
                   .build()
                   .create(AnalyticsService::class.java)
      }
    }
    
    // As a dependency of a constructor-injected class.
    class ExampleServiceImpl @Inject constructor(
      @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
    ) : ...
    
    // At field injection.
    @AndroidEntryPoint
    class ExampleActivity: AppCompatActivity() {
    
      @AuthInterceptorOkHttpClient
      @Inject lateinit var okHttpClient: OkHttpClient
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    最佳做法
    如果对某一个类型添加限定符@Qualifier,应向提供该依赖项的所有可能的方式添加限定符。避免遗漏造成注入错误的依赖项。

    @Inject 告知注入位置,@ActivityContext 预定义限定符,系统默认提供

    java注解可应用于形参(PARAMETER,)和local_paramter 本地变量

    为 Android系统类生成默认的组件

    @InstallIn 可以关联的组件

    ApplicationComponentApplication
    ActivityRetainedComponentViewModel
    ActivityComponentActivity
    FragmentComponentFragment
    ViewComponentView
    ViewWithFragmentComponent带有 @WithFragmentBindings 注释的 View
    ServiceComponentService

    示例:

    @Module
    @InstallIn(xxxComponent::class)
    class xxxModule{
    
    	@Binds@Provides
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    因为 Hilt 直接从 ApplicationComponent 注入广播接收器.

    作用域

    解决问题

    1. 限制依赖的使用范围,在限制范围内依赖只初始化一次。避免重复创建浪费资源
    2. 依赖只在某一个作用域有效。

    解决思路

    前两个需要着重查看下,hilt提供的作用域。

    ApplicationApplicationComponent@Singleton
    View ModelActivityRetainedComponent@ActivityRetainedScope
    ActivityActivityComponent@ActivityScoped
    FragmentFragmentComponent@FragmentScoped
    ViewViewComponent@ViewScoped
    ServiceServiceComponent@ServiceScoped

    hilt支持注入的类,Android类,四大组件viewModel,View

    上面介绍在构造函数中不能注入的对象或者其他不属于应用的第三方类图的对象,通过build来构建。下面介绍在Hilt不支持的类中注入依赖项

    对于不支持的类型,定义入口点绑定组件形式,然后通过类检索组件,获取组件中的类型
    比如:ContentProvider 中要使用Hilt提供的依赖项

    解决问题

    1. 不支持的类想使用hilt提供依赖对象

    解决思路:访问入口点

    1. 使用@EntryPoint注释 @InstallIn(安装类),定义接口及需要提供的对象引用。声明在不支持的类中

    示例

    class ExampleContentProvider : ContentProvider() {
      @EntryPoint
      @InstallIn(ApplicationComponent::class)
      interface ExampleContentProviderEntryPoint {
        fun analyticsService(): AnalyticsService
      }
      ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    多模块应用中使用Hilt

    DFM 如何设置依赖,通过在主模块中设置入口点(与非动态模块设置依赖方向相反),设置Dagger模块依赖主模块功能点 放入功能模块,功能模块使用drgger来构建。

    手动添加依赖注入

    1. 首先在一个类中创建所有的依赖对象,并拼装起来使用
    2. 为了复用,将拼装的逻辑抽取到服务定位器中进行全局统一管理
    3. 为了不同的界面流程需要创建不同的对象依赖。将对象的创建放入服务定位器中,并且创建工厂方法来对外提供对象
    4. 对于不同的业务流程,需要创建不同的依赖容器,并且绑定界面(Activity/Fragment)生命周期,在onCreate()创建在对应的Destroy()销毁
    5. 特殊情况需要在配置变更后保留对象状态,需要使用状态容器,生命周期绑定依赖容器和ui界面组件推荐使用生命周期管理工具LifeCycle

    优点

    1. 依赖注入对于创建可扩展且可测试的Android应用是一项合适的技术
    2. 将容器作为在应用内不同部分共享各个类的实例的一种方式,以及使用工厂创建各个类实例的集中位置
    3. 手动依赖项注入要求您手动构造每个类及其依赖项,并借助容器和工厂重复使用和管理依赖项。

    缺点

    1. 应用变大,流程变多,不同的界面流程需要维护不同的容器创建销毁,工厂方法的维护。
    2. 将会编写大量的样板代码,很容易出错,生命周期管理操作不当会产生微小错误和内存泄露
      手动注入需要写模板代码,多模块依赖顶层模块构建对象,需要依赖到不同子模块。 依赖管理,依赖范围管理,依赖生命周期管理,依赖复用。需要考虑

    问题

    1. 比方说,我有一个类A,我想通过构造函数或者传递参数方式注入类B应该怎么做?
    2. 比方说,程序代码需要用到一个外部库Retrofit或者OkHttp对象或者接口(控制翻转),怎么声明依赖关系。

    总结:

    hilt基于Dagger提供一套标准依赖注入做法,Hilt优化了Dagger依赖注入的功能,减少了样版代码的编写,依赖注入主要有两种方式,构造函数注入是最常用的,而参数注入使用优先级低于构造函数注入,对于接口,三方工具库等不能修改源码的依赖注入,需要创建Hilt模块,并使用@Module @Installin(依赖安装的类::class),@Provider(sdk的Builder构造),@Binds(接口,抽象类)注释解决依赖传递问题。

    hilt用法xmind 脑图
    在这里插入图片描述

    1. Hilt 中文介绍
    2. Dagger中文介绍

  • 相关阅读:
    html在线商城购物网站制作——基于HTML+CSS+JavaScript鲜花礼品电商网站
    原生Js显示富文本效果demo(整理)
    在实际工作中如何开展性能测试?
    虹科分享 | 超低温冷冻箱温度分布验证的9步指南
    Linux IO重定向及管道
    PYTHON专题-(11)基操之我要发邮件
    学习Java的第九天
    11个常见的分类特征的编码技术
    2.6W字系统总结,带你实现 Linux 自由!
    网络套接字2
  • 原文地址:https://blog.csdn.net/o279642707/article/details/125429055