Jetpack Hilt

Jetpack Hilt

概述

  • 依赖注入指Dependency Injection,简称DI,Hilt是一个功能强大用法简单的依赖注入框架。
  • 通俗来讲,一个类中有一个变量,这个变量就是类的依赖,通过外部注入对这个变量进行赋值,这就是依赖注入。
  • 依赖注入有利于代码解耦。

官方文档

配置Hilt

project/build.gradle

buildscript {
    ext {
        kotlin_version = '1.5.21'
        hiltVersion = "2.36"
    }
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // Hilt
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hiltVersion"
    }
}

module/build.gradle

apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation "com.google.dagger:hilt-android:$hiltVersion"
    kapt "com.google.dagger:hilt-android-compiler:$hiltVersion"
}

Hilt API介绍

常用注解说明

@HiltAndroidApp

  • 必须定义一个由 @HiltAndroidApp 注解的 Application。
  • 在编译期会触发 Hilt 的代码生成。

@AndroidEntryPoint

  • @AndroidEntryPoint 注解提供Android类的依赖。

@Inject

  • 在变量上添加 @Inject 注解,表示将实例注入到变量,在 super.onCreate() 之后被调用。
  • 在类的构造函数上添加 @Inject 注解,表示告诉 Hilt 怎么创建该类的实例。

@Module

  • 使用 @Module 注解时必须使用 @InstallIn 注解指定活动范围。
  • 用于创建依赖类的对象。

@Provides

  • 配合 @Module 注解使用。
  • 用于提供依赖项对象。

@EntryPoint

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

入口点

Hilt 支持的入口点:

  • Application
  • ViewModel
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

说明:Application 的入口点需要使用 @HiltAndroidApp 注解,ViewModel 的入口点需要使用 @HiltViewModel 注解,其他入口点使用 @AndroidEntryPoint 注解。

组件层次结构

Hilt 为绝大多数 Android 框架类生成组件 (或称为依赖项容器)。每个组件关联信息 (或称为绑定) 通过组件层次结构向下传递。

在这里插入图片描述

为Android类生成的组件

在 @InstallIn 注解中使用。

Hilt 组件注入器面向的对象
SingletonComponentApplication
ActivityRetainedComponent不适用
ViewModelComponentViewModel
ActivityComponentActivity
FragmentComponentFragment
ViewComponentView
ViewWithFragmentComponent带有 @WithFragmentBindings 注解的 View
ServiceComponentService

组件的作用域

  • @Singleton 注解就是一个作用域注解,表示在整个应用程序范围内共享实例。
  • @ActivityScoped 注解表示在Activity和内部的Fragment和View共享实例。
Android 类生成的组件作用域
ApplicationSingletonComponent@Singleton
ActivityActivityRetainedComponent@ActivityRetainedScoped
ViewModelViewModelComponent@ViewModelScoped
ActivityActivityComponent@ActivityScoped
FragmentFragmentComponent@FragmentScoped
ViewViewComponent@ViewScoped
带有 @WithFragmentBindings 注解的 ViewViewWithFragmentComponent@ViewScoped
ServiceServiceComponent@ServiceScoped

组件的生命周期

Hilt 会按照相应 Android 类的生命周期自动创建和销毁生成的组件类的实例。

生成的组件创建时机销毁时机
SingletonComponentApplication#onCreate()Application 已销毁
ActivityRetainedComponentActivity#onCreate()Activity#onDestroy()
ViewModelComponentViewModel 已创建ViewModel 已销毁
ActivityComponentActivity#onCreate()Activity#onDestroy()
FragmentComponentFragment#onAttach()Fragment#onDestroy()
ViewComponentView#super()View 已销毁
ViewWithFragmentComponentView#super()View 已销毁
ServiceComponentService#onCreate()Service#onDestroy()

组件的默认绑定

每个 Hilt 组件都附带一组默认绑定,Hilt 可以将其作为依赖项注入您自己的自定义绑定。请注意,这些绑定对应于常规 activity 和 fragment 类型,而不对应于任何特定子类。这是因为,Hilt 会使用单个 activity 组件定义来注入所有 activity。每个 activity 都有此组件的不同实例。

Android 组件默认绑定
SingletonComponentApplication
ActivityRetainedComponentApplication
ViewModelComponentSavedStateHandle
ActivityComponentApplicationActivity
FragmentComponentApplicationActivityFragment
ViewComponentApplicationActivityView
ViewWithFragmentComponentApplicationActivityFragmentView
ServiceComponentApplicationService

Hilt 使用

配置Application

定义一个 Application 并使用 @HiltAndroidApp 注解,否则 Hilt 将无法使用。

@HiltAndroidApp
class BaseApplication : Application() {
}

注册到 AndroidManifest.xml 文件中:

<application
             android:name=".BaseApplication">
</application>

在Activity中使用

  • 使用 @AndroidEntryPoint 注解,将依赖项注入 Android 类。
  • 使用 @Inject 注解的字段不能为private。

使用

class User @Inject constructor() {
    fun sayHello() {
        log("hello")
    }
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var user: User

    @Inject
    lateinit var user2: User

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        log("$user")
        log("$user2")
        user.sayHello()
        user2.sayHello()
    }
}

输出信息:

com.example.hiltdemo.entity.User@6a0ef50
com.example.hiltdemo.entity.User@3507549
hello
hello

带参数的依赖注入

定义Driver类,并在构造函数上使用 @Inject 注解。

在Car类的构造函数里增加 Driver 参数。

class Driver @Inject constructor() {
}
class Car @Inject constructor(val driver: Driver) {

    fun start() {
        log("$driver 正在开车")
    }
}

使用:

@Inject
lateinit var car: Car

car.start()

接口的依赖注入

  • 定义一个Engine接口。
  • 实现两个类GasEngine、ElectricEngine。
  • 使用 @Module 注解声明一个模块类EngineModule,使用 @Binds 注解提供依赖注入的对象。

定义接口:

interface Engine {
    fun start()
    fun shutdown()
}

实现接口:

class GasEngine @Inject constructor() : Engine {
    override fun start() {
        log("燃油引擎 start")
    }

    override fun shutdown() {
        log("燃油引擎 shutdown")
    }
}

class ElectricEngine @Inject constructor() : Engine {
    override fun start() {
        log("电力引擎 start")
    }

    override fun shutdown() {
        log("电力引擎 shutdown")
    }
}

定义Module:

返回值必须是Engine类型,表示给Engine类型的接口提供实例。

@Module
@InstallIn(ActivityComponent::class)
abstract class EngineModule {

    @Binds
    abstract fun bindEngine(gasEngine: GasEngine): Engine
}

使用:

class Car @Inject constructor(val user: User) {

    @Inject
    lateinit var engine: Engine

    fun start() {     
        engine.start()
        log("$driver 正在开车")
        engine.shutdown()
    }
}

输出信息:

燃油引擎 start
Driver@e7744ba 正在开车
燃油引擎 shutdown

相同类型注入不同的对象

  • 使用 @Qualifier 注解分别定义2个注解:BindGasEngine、BindElectricEngine。
  • 定义一个Module类,分别添加2个注解。
  • 在注入的地方使用2个注解。

定义两个注解:

用于区分类型。

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindGasEngine

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindElectricEngine

定义Module:

@Module
@InstallIn(ActivityComponent::class)
abstract class SelectEngineModule {

    @BindGasEngine
    @Binds
    abstract fun bindGasEngine(gasEngine: GasEngine): Engine

    @BindElectricEngine
    @Binds
    abstract fun bindElectricEngine(electricEngine: ElectricEngine): Engine
}

使用:

使用 @BindGasEngine 注解就会注入 GasEngine 类型的实例。

class Car @Inject constructor(val driver: Driver) {

    @BindGasEngine
    @Inject
    lateinit var gasEngine: Engine

    @BindElectricEngine
    @Inject
    lateinit var electricEngine: Engine

    fun start() {
        gasEngine.start()
        electricEngine.start()
        log("hello $driver 正在开车")
        gasEngine.shutdown()
        electricEngine.shutdown()
    }
}

输出信息:

燃油引擎 start
电力引擎 start
hello Driver@e7744ba 正在开车
燃油引擎 shutdown
电力引擎 shutdown

第三方类的依赖注入

  • 使用 @Module 注解定义Module,并使用 @InstallIn 注解定义安装至哪个组件。
  • 使用 @Provides 注解表示提供的实例。

定义Module:

@Single 注解表示单例模式,provideRetrofit() 函数中的参数 Hilt 会自动注入。

@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {

    @Singleton
    @Provides
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
        .build()
    }

    @Singleton
    @Provides
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
        .baseUrl("https://wanandroid.com/")
        .build()
    }
}

使用:

@Inject
lateinit var okHttpClient: OkHttpClient

@Inject
lateinit var retrofit: Retrofit

Application & Context

Hilt 提供了 @ApplicationContext 和 @ActivityContext 内置注解,会自动提供一个 Application 类型和 Activity 类型的 Context 注入。

使用Context类型:

class MyContext @Inject constructor(
    @ApplicationContext val appContext: Context,
    @ActivityContext val activityContext: Context
)

使用:

@Inject
lateinit var myContext: MyContext

log("appContext : ${myContext.appContext}")
log("activityContext : ${myContext.activityContext}")

输出信息:

appContext : com.example.hiltdemo.BaseApplication@e7744ba
activityContext : com.example.hiltdemo.MainActivity@756b7ec

内置依赖Application和Activity

对于 Application 和 Activity 这两个类型,Hilt 也是给它们预置好了注入功能,可以省略注解。

class MyAppAndActivity @Inject constructor(
    val application: Application,
    val activity: Activity
)

使用:

@Inject
lateinit var my: MyAppAndActivity

log("application : ${my.application}")
log("activity : ${my.activity}")

输出信息:

application : com.example.hiltdemo.BaseApplication@e7744ba
activity : com.example.hiltdemo.MainActivity@756b7ec

依赖注入自定义Application

定义Module:

@Module
@InstallIn(SingletonComponent::class)
class ApplicationModule {

    @Provides
    fun provideBaseApplication(application: Application): BaseApplication {
        return application as BaseApplication
    }
}

使用:

@Inject
lateinit var baseApplication: BaseApplication

log("baseApplication: $baseApplication")

输出信息:

baseApplication: com.example.hiltdemo.BaseApplication@e7744ba

在ViewModel中使用

  • MVVM架构中,数据层层的实例应该由ViewModel层创建。
  • ViewModel层需要使用 @HiltViewModel 注解,在ViewModel的构造函数上使用 @Inject 注解。

数据层:

在构造函数上使用 @Inject 注解,表示将 Repository 实例注入到 ViewModel中。

class Repository @Inject constructor() {

    fun getData() = "从仓库获取了数据"
}

ViewModel层

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: Repository
) : ViewModel() {

    private val liveData = MutableLiveData<String>()

    fun loadData(): MutableLiveData<String> {
        viewModelScope.launch {
            delay(5000L)
            liveData.value = repository.getData()
        }
        return liveData
    }
}

使用:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val viewModel by viewModels<MyViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.loadData().observe(this) {
            log(it)
        }
    }
}

代码下载

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值