阅读 477

Android-Jetpack-Hilt 组件 包爽攻略

Hilt 是啥?

Hilt 就是依赖Dagger2 而来的 一个 专属android 端的 依赖注入框架。Dagger2 是啥? Dagger是以前 square 做的 依赖注入框架,但是大量使用了反射,谷歌觉得这东西不错,拿来改了一下,使用编译期注解 大幅度提高性能以后 的东西就叫Dagger2了, 国外的app 多数都用了Dagger2, 但是这个框架在国内用的人很少。

依赖注入是啥?

说简单一点,如果你构造一个对象所需要的值 是别人给你的,那就叫依赖注入,如果是你自己new出来的,那就不叫依赖注入

class A1 {
    public A1(String name) {
        this.name = name;
    }

    private String name;

}

class A2 {
    public A2() {
        this.name = "wuyue";
    }

    private String name;

}
复制代码

例如上面的, A1 这个类 构造函数的时候 name的值 是外面传过来的,那这个A1对象的构建过程 就是依赖注入,因为你A1对象 是依赖 外部传递过来的值

再看A2 A2的构造函数 是直接 自己 new出来 赋值的。那自然就不叫依赖注入了。

所以依赖注入 对于大部分人来说 其实每天都在用。

既然每天都在用 那用这些依赖注入的框架有啥用?

这是个好问题, 依赖注入的技术既然每天都在用,为啥我们还要用 这些什么Dagger2 Hilt 之类的依赖注入框架呢? 其实原因就是 你用了这些所谓的依赖注入框架 可以让你少写很多代码,且变的很容易维护

你在构造一个对象的时候 如果是手动new 出来的,那么如果日后这个对象的构造方法发生了改变,那么你所有new 的地方 都要挨个修改,这岂不是很麻烦? 如果有依赖注入框架帮你处理 那你其实只要改一个地方就可以了。

第一个简单的例子

在这个例子中,我们熟悉一下Hilt的基本用法。

首先在root project 中的 dependencies 加入依赖

 classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
复制代码

然后在你的app工程中

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

implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
复制代码

自定义Application 注意要加注解了。

@HiltAndroidApp
class MyApplication:Application() {

}
复制代码

首先定义一个class

data class Person constructor(val name: String, val age: Int) {
    @Inject
    constructor() : this("vivo", 18)
}
复制代码

注意 这个class 中 使用了 Inject注解 其实就是告诉 Hilt 如何来提供这个对象

然后写我们的activity 页面

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var person: Person

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.v("wuyue", "person:$person")
    }
}
复制代码

注意 要加入 @AndroidEntryPoint 这个注解,同样的你 声明person对象的时候也一样 要使用Inject注解。

这就是一个最简单的Hilt的例子

好处是显而易见的, 比方说 以后Person的使用 可以不用那么写了,直接Inject 就可以 我压根不用关心 这个Person对象是怎么被构造出来的,以后构造函数发生了改变 调用的地方 也不用修改代码。

当然了,这里有人会说, 你这我虽然明白了优点,但是实际android编程中 没人这么用呀,

有没有更好的例子呢?

获取 Retrofit/Okhttp 对象

通常来说,我们一个项目里面,总会有网络请求,这些网络请求 都会有一些 基础的Retrofit或者是Okhttp的对象, 我们很多时候都会写成单例 然后去get他们出来, 有没有更简便的写法? 有的

//retrofit的 service
interface BaiduApiService{

}

@Module
@InstallIn(ActivityComponent::class)
object BaiduApiModule{

    @Provides
    fun provideBaiduService():BaiduApiService{
        return Retrofit.Builder().baseUrl("https://baidu.com").build().create(BaiduApiService::class.java)
    }
}
复制代码

然后:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var baiduApiService: BaiduApiService

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

    }
}

复制代码

即可。仔细体会体会 这种依赖注入框架的 写法 是不是比你之前 单例的写法要简洁方便了很多?

这里解释一下几个注解的含义

@Module 多数场景下 用来提供构造 无法使用@Inject的依赖,啥意思?

第一个例子中 Person 这个class 是我们自己写的吧,构造函数 前面 我们可以加入Inject 注解

但是例如像Retrofit这样的第三方库 ,我们拿不到他们的代码呀, 又想用 Hilt,怎么办呢

自然就是这个Module了,另外用module 的 时候,一般还要配合使用InstallIn 注解,后面跟的参数值 是用来指定module的范围的

可以看下有多少个范围

image.png

最后 就是 @Provides 这个注解, 这个很简单

一般也是用来 和@Module 一起配合的。 你哪个函数 提供了依赖注入 你就在这个函数上加入这个注解就可以了。

多对象 细节不同 怎么处理

举个例子 一个项目里面 可以有多个okhttp的client对吧,有的接口 我们要做一个拦截器 比如说打印一些埋点, 有些接口 我们要做一个拦截器 来判断下登录态是否失效,不一样的场景,我们需要 new不同的okhttp client 那有没有更简便的写法,答案是有的!

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

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class CheckTokenOkHttpClient
复制代码

我们先用Qualifier 限定符 来标记一下

@Module
@InstallIn(ActivityComponent::class)
object OkHttpModule {
    @DataReportsOkHttpClient
    @Provides
     fun provideDataReportInterceptorOkHttpClient(
        dataReportInterceptor: DataReportInterceptor
    ): OkHttpClient {
        return OkHttpClient.Builder().addInterceptor(dataReportInterceptor).build()
    }

    @CheckTokenOkHttpClient
    @Provides
     fun provideCheckTokenInterceptorOkHttpClient(
        checkTokenInterceptor: CheckTokenInterceptor
    ): OkHttpClient {
        return OkHttpClient.Builder().addInterceptor(checkTokenInterceptor).build()
    }
}
复制代码

然后这里 provides的方法 注意了 要加上我们前面的我们先用Qualifier 标记, @DataReportsOkHttpClient

但是到这里还没完,这里一定注意一个原则:

使用Hilt的依赖注入组件 他自己的依赖 也必须是Hilt提供的,啥意思?

你看这里 我们2个provide 方法都需要一个参数 这个参数是干嘛的?就是函数参数 是一个okhttp的interceptor 对吧 ,

但是因为我们这里是依赖注入的模块,所以你使用的参数也必须是依赖注入提供的,

所以这里你如果拦截器 这么写:


class DataReportInterceptor  : Interceptor {
  
    override fun intercept(chain: Interceptor.Chain): Response {
        return chain.proceed(chain.request())
    }

}
复制代码

那是编译不过的,因为Hilt组件 不知道你这个对象 应该如何去哪里构造,所以这里你必须也指定 这个拦截器的构造 是Hilt 注入的。

所以你只要这么改就可以了:

class DataReportInterceptor  @Inject constructor() : Interceptor {
    init {
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        return chain.proceed(chain.request())
    }

}

class CheckTokenInterceptor @Inject constructor()  : Interceptor {

    init {
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        return chain.proceed(chain.request())
    }

}


复制代码

这样Hilt 就知道要去哪里 取这个依赖了。(这个地方官方文档竟然没有提到,导致很多人照着官方文档写demo 一直报错)

一些小技巧

android中 构造很多对象 都需要Context,Hilt 默认为我们实现了这种Context,不需要我们再费尽心思 构造Context了(实际上你也很难构造处理 因为Context 是无法 new出来的)

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

对应的当然还有Application的Context

此外 我们还可以限定 这些依赖注入对象的 作用域

image.png

大家有兴趣可以去官网查看一下。很简单,就不演示了。

其实就是 你对象的作用与 如果是Activity 那么 fragment和view 肯定可以获取到 并且共享他的状态

能理解Activity》Fragment》View 那就很容易理解了。

到底为啥要用Hilt呀

我们学了前面的基础例子以后 一定要把这个问题想明白,否则这个框架你是无法真正理解的,理解他以后 才能用得好。

Hilt要解决的问题就是:

在android开发中,我们太多的场景是干啥?是在Activity里面 构造对象,而这些对象我们是怎么构建出来的?

大部分人都是New出来的对吧,但是这些New出来的对象 所属的Class 一旦发生了构造函数的变更,

我们还得去找出所有 引用这个Class 的 地方 11 去修改 调用方法。 这个就很不方便了。

回顾下前面我们的例子,使用Hilt的话 可以极大避免这种场景。

文章分类
Android
文章标签