使用Hilt完成依赖注入,让你的安卓代码层次有几层楼那么高(四)

1,881 阅读4分钟

我正在参加「掘金·启航计划」

上一篇文章的快捷入口:写文章 - 使用Hilt完成依赖注入,让你的安卓代码层次有几层楼那么高(三) - 掘金 (juejin.cn)

辅助注入

如果通过看前面的文章学会了Hilt的基础使用,那么恭喜你,你基本已经可以在项目中展开使用Hilt框架为你的项目提供自动注入功能了,但是有一些场景你可能会感到疑惑,回顾之前的注入,你会发现一个问题:当使用Hilt构建一个对象的时候,在定义对象的时候,就要求所有参数已经被确定,但是实际场景中,我们有许多参数是无法在定义对象的阶段就被确定的,一些参数需要在项目运行时才能被确定,例如网络请求的结果等,这些动态的参数让我们无法使用Hilt为我们注入,至少目前还不行。

为了解决这些动态参数导致的问题,dagger2(即Hilt的底层框架)提供了一种解决问题的方案:辅助注入

辅助注入是一种依赖注入 (DI) 模式,用于构造一个对象,其中一些参数可能由 DI 框架提供,而其他参数必须在创建时由用户传入(也称为“辅助”)。 工厂通常负责组合所有参数并创建对象。

依然是废话不多说环节,我们使用具体的案例来讲解如何使用辅助注入


data class Wheel(
    val name:String
)

data class Engine(
    val name:String
)

class Car(
    val wheel:Wheel,
    val engine:Engine,
    val number:Int
)

假设我们有轮子、引擎和汽车三个实体类,其中汽车是由轮子和引擎组成的,我们希望创建一个工厂,自动为我们生成某个类型的汽车,因此轮子和引擎的构建方式是固定的,但是汽车的编号每个汽车是不同的。

按照之前的文档,我们编写Module类,但是我们会发现,我们无法在定义provides方法阶段就定义好number。

@InstallIn(SingletonComponent::class)
@Module
object JiLiCarModule{

    @Singleton
    @Provides
    fun provideJiLiWheel():Wheel{
        return Wheel("吉利牌车轮")
    }

    @Singleton
    @Provides
    fun provideJiLiEngine():Engine{
        return Engine("吉利牌引擎")
    }

    @Provides
    fun provideJiLiCar(
        wheel: Wheel,
        engine: Engine
    ):Car{
        return Car(
            wheel = wheel,
            engine = engine,
            //👇🏻出问题的部分,我们无法明确这个编号
            number = ???
        )
    }

}

也许某些情况下,我们确实能在定义provides方法的时候就明确number的生成方式(例如随机数的业务场景),但是我们要讨论的是在定义provides方法阶段无法明确参数的情况,因此我们需要dagger提供的辅助注入来帮助我们完成这种场景下的Hilt注入。

动手实战

一、为汽车实体类添加@AssistedInject注解,同时为动态传入的参数添加@Assisted注解,这里是number。

class Car @AssistedInject constructor(
    val wheel:Wheel,
    val engine:Engine,
    @Assisted
    val number:Int
)

二、构建工厂类,使用@AssistedFactory注解表示这是一个辅助注入工厂类,同时编写需要注入的实体类的构建方法,方法只需要动态传入的参数,在这里指的是number

@AssistedFactory
interface JiLiCarFactory{
    
    fun createJiLiCar(
        number:Int
    ):Car
    
}

三、注入工厂类,这里和之前直接注入对象使用方法基本是一致的,区别只有一个,这里注入的是工厂

@AndroidEntryPoint
class YouFragment : Fragment() {

    //注入工厂类
    @Inject
    private lateinit var jiLiCarFactory: JiLiCarFactory
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //使用工厂类,传入动态参数构建具体的对象
        val jiLiCar: Car =jiLiCarFactory.createJiLiCar(114514)
    }
    
}

四、解决相同类型参数注入问题

当你有多个相同类型的参数的时候,Hilt会不知道如何注入,例如我们的实体类是这样的情况:

class Car @AssistedInject constructor(
    val wheel:Wheel,
    val engine:Engine,
    @Assisted
    val number:Int,
    @Assisted
    val number2:Int
)

这里出现了两个相同类型(Int)的参数,因此我们需要区分开来,这里使用注解@Assisted的参数来区分,改造为如下:

class Car @AssistedInject constructor(
    val wheel:Wheel,
    val engine:Engine,
    @Assisted("number")
    val number:Int,
    @Assisted("number2")
    val number2:Int
)

同时别忘了为工厂的方法也添加对应的注解,否则Hilt一样无法知道如何映射对应的参数

@AssistedFactory
interface JiLiCarFactory{

    fun createJiLiCar(
        @Assisted("number") number:Int,
        @Assisted("number2") number2:Int
    ):Car

}

工厂类用法和原来的保持一致,只是多加了参数

val jiLiCar=jiLiCarFactory.createJiLiCar(114514,1111)

总结

好的,关于Hilt的系列就到此结束了,笔者写这系列的初衷是帮助那些刚刚使用Hilt的新手快速入门并动手使用Hilt,并没有做太深入的分析(笔者的程度也没有到那个层次,就不班门弄斧了),同时也为很久没有更新文章抱歉,近期会恢复更新频率,同时也会谈谈compose相关的话题,敬请期待。