我正在参加「掘金·启航计划」
上一篇文章的快捷入口:写文章 – 使用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)data class Wheel( val name:String ) data class Engine( val name:String ) class Car( val wheel:Wheel, val engine:Engine, val number:Int )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)@Moduleobject JiLiCarModule{@Singleton@Providesfun provideJiLiWheel():Wheel{return Wheel("吉利牌车轮")}@Singleton@Providesfun provideJiLiEngine():Engine{return Engine("吉利牌引擎")}@Providesfun provideJiLiCar(wheel: Wheel,engine: Engine):Car{return Car(wheel = wheel,engine = engine,//??出问题的部分,我们无法明确这个编号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 = ??? ) } }@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,@Assistedval number:Int)class Car @AssistedInject constructor( val wheel:Wheel, val engine:Engine, @Assisted val number:Int )class Car @AssistedInject constructor( val wheel:Wheel, val engine:Engine, @Assisted val number:Int )
二、构建工厂类,使用@AssistedFactory
注解表示这是一个辅助注入工厂类,同时编写需要注入的实体类的构建方法,方法只需要动态传入的参数
,在这里指的是number
@AssistedFactoryinterface JiLiCarFactory{fun createJiLiCar(number:Int):Car}@AssistedFactory interface JiLiCarFactory{ fun createJiLiCar( number:Int ):Car }@AssistedFactory interface JiLiCarFactory{ fun createJiLiCar( number:Int ):Car }
三、注入工厂类,这里和之前直接注入对象使用方法基本是一致的,区别只有一个,这里注入的是工厂
@AndroidEntryPointclass YouFragment : Fragment() {//注入工厂类@Injectprivate lateinit var jiLiCarFactory: JiLiCarFactoryoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//使用工厂类,传入动态参数构建具体的对象val jiLiCar: Car =jiLiCarFactory.createJiLiCar(114514)}}@AndroidEntryPoint class YouFragment : Fragment() { //注入工厂类 @Inject private lateinit var jiLiCarFactory: JiLiCarFactory override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //使用工厂类,传入动态参数构建具体的对象 val jiLiCar: Car =jiLiCarFactory.createJiLiCar(114514) } }@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,@Assistedval number:Int,@Assistedval number2:Int)class Car @AssistedInject constructor( val wheel:Wheel, val engine:Engine, @Assisted val number:Int, @Assisted val number2:Int )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)class Car @AssistedInject constructor( val wheel:Wheel, val engine:Engine, @Assisted("number") val number:Int, @Assisted("number2") val number2:Int )class Car @AssistedInject constructor( val wheel:Wheel, val engine:Engine, @Assisted("number") val number:Int, @Assisted("number2") val number2:Int )
同时别忘了为工厂的方法也添加对应的注解,否则Hilt一样无法知道如何映射对应的参数
@AssistedFactoryinterface JiLiCarFactory{fun createJiLiCar(@Assisted("number") number:Int,@Assisted("number2") number2:Int):Car}@AssistedFactory interface JiLiCarFactory{ fun createJiLiCar( @Assisted("number") number:Int, @Assisted("number2") number2:Int ):Car }@AssistedFactory interface JiLiCarFactory{ fun createJiLiCar( @Assisted("number") number:Int, @Assisted("number2") number2:Int ):Car }
工厂类用法和原来的保持一致,只是多加了参数
val jiLiCar=jiLiCarFactory.createJiLiCar(114514,1111)val jiLiCar=jiLiCarFactory.createJiLiCar(114514,1111)val jiLiCar=jiLiCarFactory.createJiLiCar(114514,1111)
总结
好的,关于Hilt的系列就到此结束了,笔者写这系列的初衷是帮助那些刚刚使用Hilt的新手快速入门并动手使用Hilt,并没有做太深入的分析(笔者的程度也没有到那个层次,就不班门弄斧了),同时也为很久没有更新文章抱歉,近期会恢复更新频率,同时也会谈谈compose相关的话题,敬请期待。