Hilt入门

作者: 如沙雨下 | 来源:发表于2020-12-30 20:41 被阅读0次

    Hilt 是什么

    Hilt 是 Android 的依赖项注入库,可减少在项目中执行手动依赖项注入的样板代码。执行手动依赖项注入要求您手动构造每个类及其依赖项,并借助容器重复使用和管理依赖项。

    Hilt 通过为项目中的每个 Android 类提供容器并自动管理其生命周期,提供了一种在应用中使用 DI(依赖项注入)的标准方法。Hilt 在热门 DI 库 Dagger 的基础上构建而成,因而能够受益于 Dagger 的编译时正确性、运行时性能、可伸缩性和 Android Studio 支持

    依赖注入是什么

    一个类里面有一个变量,这个变量就是这个类的依赖。然后通过外部注入对这个变量进行赋值,这种就叫做依赖注入

    工厂、Builder模式、带参数的构造函数这些都属于依赖注入

    引入Hilt

    首先,将 hilt-android-gradle-plugin 插件添加到项目的根级 build.gradle 文件中:

    buildscript {
       ...
       dependencies {
       ...
       classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
     }
    }
    

    然后,应用 Gradle 插件并在 app/build.gradle 文件中添加以下依赖项:

    apply plugin: 'kotlin-kapt'
    apply plugin: 'dagger.hilt.android.plugin'
    android {
       ...
    }
    
    dependencies {
       implementation "com.google.dagger:hilt-android:2.28-alpha"
       kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
    }
    

    Hilt 使用 Java 8 功能。如需在项目中启用 Java 8,请将以下代码添加到 app/build.gradle 文件中:

    android {
     ...
     compileOptions {
     sourceCompatibility JavaVersion.VERSION_1_8
     targetCompatibility JavaVersion.VERSION_1_8
     }
    }
    

    Hilt基本使用

    • @HiltAndroidApp
      @HiltAndroidApp 将会触发 Hilt 的代码生成,作为程序依赖项容器的基类
      生成的 Hilt 依附于 Application 的生命周期,他是 App 的父组件,提供访问其他组件的依赖
      所有使用 Hilt 的应用都必须包含一个带有 @HiltAndroidApp 注释的 Application 类。
      @HiltAndroidApp 会触发 Hilt 的代码生成操作,生成的代码包括应用的一个基类,该基类充当应用级依赖项容器。

      @HiltAndroidApp
      class ExampleApplication : Application() { ... }
      
    • 在 Application 中配置好后,就可以使用 Hilt 提供的组件了;组件包含 Application,Activity,Fragment,View,Service 等。

    • @AndroidEntryPoint
      创建一个依赖容器,该容器遵循 Android 的生命周期类,目前支持的类型是: Activity, Fragment, View, Service, BroadcastReceiver.

      @AndroidEntryPoint
      class MainActivity : AppCompatActivity() 
      
    • @Inject
      使用 @Inject 来告诉 Hilt 如何提供该类的实例,常用于构造方法,非私有字段,方法中。
      Hilt 有关如何提供不同类型的实例信息也称之为绑定

      @Inject
      lateinit var orderManager: OrderManager
      class OrderManager @Inject constructor(private val addressInfo: AddressInfo) {
      
         fun payOrder() {
            println( "payOrder()执行了")
         }
      }
      

    接口注入

    • @Module
      module 是用来提供一些无法用构造@Inject 的依赖,如第三方库,接口,build 模式的构造等
      使用 @Module 注解的类,需要使用 @InstallIn 注解指定 module 的范围
      增加了 @Module 注解的类,其实代表的就是一个模块,并通过指定的组件来告诉在那个容器中可以使用绑定安装。

    • @InstallIn
      使用 @Module 注入的类,需要使用 @InstallIn 注解指定 module 的范围。
      例如使用 @InstallIn(ActivityComponent::class) 注解的 module 会绑定到 activity 的生命周期上。

      @Module
      @InstallIn(ApplicationComponent::class)
      abstract class PayModule
      
    • @Binds:必须注释一个抽象函数,抽象函数的返回值是实现的接口。通过添加具有接口实现类型的唯一参数来指定实现。

    首先需要一个接口,和一个实现类

    interface PayMethod {
        fun startPay()
    }
    
    class WxPay @Inject constructor() : PayMethod {
         override fun startPay() {
         println("WxPay start.")
       }
    }
    

    接着就需要新建一个 Module。用来实现接口的注入

    @Module
    @InstallIn(ApplicationComponent::class)
    abstract class PayModule {
       @Binds
       abstract fun bindWxPay(wxPay: WxPay): PayMethod
    }
    

    注意:这个 Module 是抽象的。

    使用如下:

     @Inject
     lateinit var wxPay: PayMethod
    
     fun payOrder() {
       wxPay.startPay()
       println("print address ${addressInfo.province}")
       wxPay.getPayResult()
     }
    

    给相同类型注入不同的实例

    还是上面的 PayMethod 接口,有两个不同的实现,如下:

    class WxPay @Inject constructor() : PayMethod {
         override fun startPay() {
         println("WxPay start.")
    }
    
     override fun getPayResult() {
         println("WxPay getPayResult.")
     }
    }
    
    class ZfbPay @Inject constructor() : PayMethod {
    
     override fun startPay() {
       println("ZfbPay start.")
     }
    
     override fun getPayResult() {
       println("ZfbPay getPayResult.")
     }
    }
    

    接着定义两个注解

    @Qualifier
    annotation class BindWxPay
    
    @Qualifier
    annotation class BindZfbPay
    

    然后修改 Module ,在 module 中用来标记相应的依赖。

    @Module
    @InstallIn(ApplicationComponent::class)
    abstract class PayModule {
    
       @BindWxPay
       @Binds
       abstract fun bindWxPay(wxPay: WxPay): PayMethod
    
       @BindZfbPay
       @Binds
       abstract fun bindZfbPay(zfbPay: ZfbPay): PayMethod
    }
    

    最后使用如下:

     @BindWxPay
     @Inject
     lateinit var wxPay: PayMethod
    
     @BindZfbPay
     @Inject
     lateinit var zfbPay: PayMethod
    
     fun payOrder() {
       wxPay.startPay()
       zfbPay.startPay()
       wxPay.getPayResult()
       zfbPay.getPayResult()
     }
    

    Hilt还提供了2个预置Qualifier限定符@ActivityContext和@ApplicationContext ,可以直接作为@Provides方法或@Inject构造的参数使用。

    class AnalyticsAdapter @Inject constructor(
    
       @ActivityContext private val context: Context,
    
       private val service: AnalyticsService
    
    ) { ... }
    

    第三方组件注入

    • @Provides
      常用于被 @Module 注解标记类的内部方法上。并提供依赖项对象。

    Hilt 支持最常见的 Android 类 Application、Activity、Fragment、View、Service、BroadcastReceiver 等等,但是您可能需要在Hilt 不支持的类中执行依赖注入,在这种情况下可以使用 @EntryPoint 注解进行创建,Hilt 会提供相应的依赖。

    Hilt 中的组件(Compenent)、生命周期、作用域

    使用 @Module 注解的类,需要使用 @Installin 注解来指定 module 的范围。

    例如 @InstallIn(ApplicationComponent::class) 注解的 Module 就会绑定到 Application 的生命周期上。

    Hilt 提供了以下组件来绑定依赖与对应 Android 类的活动范围

    Hilt 组件 对应 Android 类活动的范围
    ApplicationComponent Application
    ActivityRetainedComponent ViewModel
    ActivityComponent Activity
    FragmentComponent Fragment
    ViewComponent View
    ViewWithFragmentComponent View annotated with @WithFragmentBindings
    ServiceComponent Service

    Hilt 没有为 broadcast receivers 提供组件,因为 Hilt 直接进从 ApplicationComponent 中注入 broadcast receivers。


    Hilt 会根据相应的 Android 类生命周期自动创建和销毁组件的实例,对应关系如下:

    Hilt 提供的组件 创建对应的生命周期 结束对应的生命周期 作用域
    ApplicationComponent Application#onCreate() Application#onDestroy() @Singleton
    ActivityRetainedComponent Activity#onCreate() Activity#onDestroy() @ActivityRetainedScope
    ActivityComponent Activity#onCreate() Activity#onDestroy() @ActivityScoped
    FragmentComponent Fragment#onAttach() Fragment#onDestroy() @FragmentScoped
    ViewComponent View#super() View destroyed @ViewScoped
    ViewWithFragmentComponent View#super() View destroyed @ViewScoped
    ServiceComponent Service#onCreate() View destroyed @ViewScoped

    @InstallIn模块中确定绑定范围时,绑定上的范围必须与component范围匹配。例如,@InstallIn(ActivityComponent.class)模块内的绑定只能用限制范围@ActivityScoped

    例如我们需要在App中共享OkHttp的配置:

    @Module
    @InstallIn(ApplicationComponent::class)
    class NetworkModule {
    
       @Singleton
       @Provides
       fun provideOkHttpClient(): OkHttpClient {
       return OkHttpClient.Builder()
           .connectTimeout(20, TimeUnit.SECONDS)
           .readTimeout(20, TimeUnit.SECONDS)
           .writeTimeout(20, TimeUnit.SECONDS)
           .build()
       }
    }
    

    默认情况下,Hilt 中的所有绑定都未限定作用域。这意味着,每当应用请求绑定时,Hilt 都会创建所需类型的一个新实例

    ViewModel

    图 1. Android 应用的应用图表模型

    各个类之间的依赖关系可以表示为图表,其中每个类都连接到其所依赖的类。所有类及其依赖关系的表示法便构成了应用图表。在图 1 中,您可以看到应用图表的抽象呈现。当 A 类 (ViewModel) 依赖于 B 类 (Repository) 时,有一条从 A 指向 B 的直线表示该依赖关系。

    依赖项注入有助于建立这些链接并使您可以更换实现以进行测试。例如,在测试依赖于代码库的 ViewModel 时,您可以通过伪造或模拟传递 Repository 的不同实现,以测试不同的情形。

    对于ViewModel这种常用Jetpack组件,Hilt专门为其提供了一种独立的依赖注入方式

    dependencies {
       ...
       implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
       kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
    }
    
    
    class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel() {
    
       fun doWork() {
           repository.doRepositoryWork()
       }
    }
    

    参考资料:https://developer.android.google.cn/training/dependency-injection/hilt-android

    相关文章

      网友评论

        本文标题:Hilt入门

        本文链接:https://www.haomeiwen.com/subject/wqckoktx.html