使用Hilt注入不能修改构造方法的类
在上一节中,在向Hilt说明如何注入需要的类的时候,我们是通过直接修改类的构造方法的方式来实现的,那么实际开发中存在多种情况,是我们无法修改构造方法的,那么如何完成这个任务呢?答案是自定义我们的模块,这里的模块指的是带有 @Module 注释的类,这个模块的意义跟@Inject注释加在构造函数中的意义是一样的,都是向Hilt说明注入对象的方式。
1.注入一个接口(Bind方式)
假设一个这样的场景,activity需要使用一个TestInterface的接口实例,实现则来自TestImpl,按照第一章的方法,我们无法使用@Inject去修饰接口的构造参数,因为接口根本没有构造参数。
@AndroidEntryPoint class ExampleActivity:AppCompatActivity() { @Inject lateinit var testInterface: TestInterface override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) testInterface.doSomeThing() } } interface TestInterface{ fun doSomeThing() } //接口实现 class TestImpl:TestInterface{ override fun doSomeThing() { Log.d("日志","做了一些事") } }
于是,我们新建一个Hilt容器,使用@Module注解,注意容器必须是抽象类。然后我们新建一个方法,方法也必须是抽象方法,方法使用@Bind注解,方法参数为接口的实现,方法的返回值为我们需要注入的方法。
@Module //引用生成在Activity上的组件,关于这个后续文章细讲 @InstallIn(ActivityComponent::class) abstract class TestModule { @Binds abstract fun bindTestInterface( //接口的实例 testImpl: TestImpl ): TestInterface //需要绑定的接口 }
写完容器之后,接口左侧代码的位置出现了我们熟悉的小图标,点击图标,发现跳转的正是我们刚刚实现的容器的代码块区域,这反映了接口的注入源是我们自己实现的容器。
2.注入一个不能修改的类(Provide方式)
不能修改的类是什么意思呢,也就是说不是本项目创造的类,我们并不能直接修改其构造方法,例如String、okhttp、Gson等,和注入接口一样,我们同样是创造Hilt容器的方式,和注入接口有点不同,容器不能是抽象类,因为我们需要具体的方法体,方法使用@Provide注解。
方法的参数是构建当前对象需要的其他依赖(待会会讲),方法的返回值是我们要注入的类型,方法体则是注入的方式,下面以okhttpClient为例子。
@AndroidEntryPoint class ExampleActivity:AppCompatActivity() { @Inject lateinit var okHttpClient: OkHttpClient override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } @Module @InstallIn(ActivityComponent::class) object OkHttpClientModule{ @Provides fun provideOkHttpClient():OkHttpClient{ return OkHttpClient() } }
刚才上面提到了,Provide方法的参数是构建当前对象需要的其他依赖,这是如何理解呢,例如我们在构建OKhttpClient的时候,肯定是需要其他参数的,我们以cookiejar为例子展示如何在Provide方法中传入其他参数。
假设我们有一个这样的CookieJar,我们需要把他传入到OKhttpClient构造过程中去,首先我们需要向Hilt声明如何构建一个CookieJar实例,根据第一章以及第二章的内容,我们有三种方式来完成:
1.在构造函数上添加一个注解
2.使用Binds方式
3.使用Provides方式
//需要被注入到OkHttpClient中的CookieJar实例 class TestCookiejar : CookieJar{ override fun loadForRequest(url: HttpUrl): List<Cookie> { TODO("Not yet implemented") } override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) { TODO("Not yet implemented") } }
下面将分别演示三种方法的代码:
1.构造函数上添加注解
class TestCookiejar @Inject constructor(): CookieJar{ //...略 }
2.Binds方式
@Module @InstallIn(ActivityComponent::class) abstract class CookieJarModule{ @Binds abstract fun bindsCookieJar(testCookiejar: TestCookiejar):CookieJar }
3.Provides方式
@Module @InstallIn(ActivityComponent::class) object CookieJarModule2{ @Provides fun providesCookieJar():CookieJar{ return TestCookiejar() } }
你可以选择以上任意一种,他们的目的都只有一个:向Hilt说明你要如何构建一个CookieJar的实例,选择其中一个放入到代码中,然后我们回到OkHttpClient的容器代码中,在方法中添加一个CookieJar参数。
添加了参数之后,参数左侧又出现了一个小图标,说明方法中的这个参数已经成功被容器提供了,一切都不需要我们手动传入,只需要向Hilt说明即可。同样的,我们回到OkHttpClient在Activity是如何被获取的。
没有发生任何变化!因为我们只需要告诉Hilt:我要在某个Activity中获取一个OkHttpClient,一切的都交给Hilt即可,哪怕OkHttpClient的构建过程发生了变化(例如新增了一个CookieJar),对于最上层的获取者(Activity)来说,都是无感的,非常的轻耦合!