作者:newki
转载地址:https://juejin.cn/post/7129673928460468254
为什么要使用Hilt依赖注入
为什么要使用依赖注入?直接new对象不香吗?为什么要把简单的问题复杂化?
你是不是在炫技,是不是像装13?
这还真不是,如果说我使用的Dagger2,还真是炫技,NB啊。Dagger的坑也是真的多,能在大项目中把Dagger用好,那还真是牛,但是我们现在都用Hilt了有什么可装的,使用是真的简单,都是一些场景化的东西,一些固定用法。没必要没必要。
回归正题,为什么要使用依赖注入?哪种情况下推荐使用依赖注入?就算要用依赖注入,为什么依赖注入推荐使用Hilt?
一、自动管理(灵活与解耦)
首先不是说大家写项目就一定要使用依赖注入,如果大家的项目不是大项目,总共就5、6个,10多个页面,你没必要上依赖注入框架,如果是大项目,分模块,分组件,多人协同开发的,并且可能依赖的对象很复杂,或者说套娃似的对象依赖,那么使用Hilt就非常方便。不同模块/组件的开发人员直接在他自己的组件/模块下定义好对象的提供方式,另一边直接用即可,无需关系依赖的复杂度,和实现的逻辑。
我们先看看一些复杂的嵌套依赖,比如我们来一个三层套娃的依赖:
@Singleton
class UserServer @Inject constructor(private val userDao: UserDao) {
fun testUser() {
YYLogUtils.w(userDao.printUser())
toast(userDao.printUser())
}
fun getDaoContent():String{
return userDao.printUser()
}
}
@Singleton
class UserDao @Inject constructor(private val user: UserBean) {
fun printUser(): String {
return user.toString()
}
}
data class UserBean(
val name: String,
val age: Int,
val gender: Int,
val languages: List<String>
)
其他三个类都是必须了,其实也就多了一个这个类,提供UserBean对象
@Module
@InstallIn(SingletonComponent::class)
class Demo10DIModule {
@Singleton
@Provides
fun provideUser(): UserBean {
return UserBean("newki", 18, 1, listOf("中文", "英文"))
}
}
使用:
@AndroidEntryPoint
class Demo10DIActivity : BaseVMActivity() {
@Inject
lateinit var userServer: UserServer
override fun getLayoutIdRes(): Int = R.layout.activity_demo10_di
override fun init() {
findViewById<Button>(R.id.btn_01).click {
YYLogUtils.w(userServer.toString())
userServer.testUser()
}
}
如果不使用Hilt,自己new对象也能实现
class Demo10DIActivity : BaseVMActivity() {
lateinit var userServer: UserServer
override fun getLayoutIdRes(): Int = R.layout.activity_demo10_di
override fun init() {
//自己new三个对象
userServer = UserServer(UserDao(UserBean("newki", 18, 1, listOf("中文", "英文"))))
findViewById<Button>(R.id.btn_01).click {
YYLogUtils.w(userServer.toString())
userServer.testUser()
}
}
这样new出来的对象,且不说生命周期是跟随页面的,无法保持单例,我们就说如果需求变了,UseDao中需要UserBean和UserProfile2个对象了,如果你是new对象,那么就要到处修改,如果是Hilt的方式,我们就只需要修改UserDao对象的构造即可
@Singleton
class UserDao @Inject constructor(private val user: UserBean,private val profile:UserProfile) {
fun printUser(): String {
return user.toString()
}
}
以上只是举个简单例子,构建一个对象,还要构建一堆其他的对象,并且其他对象的构建同样复杂,并且必须按顺序构建,而且需要的对象的生命周期都不一样,有些生命周期可能和Activity一样,有些可能是单例,所以在构建的时候还要考虑对象声明周期,考虑对象的来源。
特别是在大型项目中很痛苦,因为项目不是一个人写的,大家协同合作开发,看别人的代码也和看天书一样,并不知道同事的对象是如何创建的,如果一个对象的构建方式发生改变,会影响整个的构建过程以及所关联的代码,牵一发而动全身。
这个时候依赖注入框架就派上用场了,我们只用专注于怎么实现功能,对象的依赖关系和生命周期,都让它来帮我们管理,一个Inject,它会按照依赖关系帮我们注入我们需要的对象,并且它会管理好每个对象的生命周期,在生命周期还没结束的情况下是不会重复new的。
所以Hilt依赖注入非常适合大项目,小项目开发者因为项目复杂度低,没遇到这些问题,所以不会理解为什么要用Hilt依赖注入,发出疑问,为什么要让简单的new对象搞这么复杂。
二、生命周期控制
这里说对象的生命周期,其实就是在一定作用域的生命周期,如果只是说单例有点太浅薄,可以说是是在一定范围内的单例。
我们直接new对象是无法控制生命周期的,除非我们使用全局单例的对象,而通过Hilt依赖注入我们可以很方便的实现对象的生命周期的控制。
比如我们普通对象的快速注入方式,直接注解Singleton就标注的是全局范围单例
@Singleton
class UserServer @Inject constructor(private val userDao: UserDao) {
fun testUser() {
YYLogUtils.w(userDao.printUser())
toast(userDao.printUser())
}
fun getDaoContent():String{
return userDao.printUser()
}
}
另一种用法是我们使用Module的方式定义依赖注入,那么使用SingletonComponent + Singleton 同样是全局范围单例的意思
@Module
@InstallIn(SingletonComponent::class)
class Demo10DIModule {
@Singleton
@Provides
fun provideUser(): UserBean {
return UserBean("newki", 18, 1, listOf("中文", "英文"))
}
}
如果我们想Activity内的单例,我们使用ActivityComponent + ActivityScoped 就是Activity范围的单例。
@Module
@InstallIn(ActivityComponent::class)
class Demo10DIModule {
@ActivityScoped
@Provides
fun provideUser(): UserBean {
return UserBean("newki", 18, 1, listOf("中文", "英文"))
}
}
以上两种都是比较常用的作用域,其他的我们还能保证Fragment内单例,View内部单例等等,用法都是同样的用法。
所以对依赖对象的生命周期控制也是Hilt很方便的一个特点,使用new对象是无法做到这一点的。
三、对比其他依赖注入
目前Android主流的也就是三种依赖注入 Dagger2 Hilt Koin。
之前比较过Koin,只能在Kotlin语言环境中使用。并且性能并不会好过Hilt。错误提示也不友好。
Dagger2不是不能用,17年18年的时候特别火,但是学习成本很高,每次创建UI个依赖注入类还得mackproject,并且错误的提示也不友好,
其实我17年就已经在使用Dagger2了,然后自己做了Demo与框架封装,后来做项目中并没有使用,一个是坑太多,一个是同事不会用,学习成本太高。也就放弃使用Dagger2了。
而Hilt其实就是Daggert2的Android场景化的实现,内部对Dagger2进行了封装,在Android开发中使用Hilt更加的简便,学习成本很低,错误提示友好。并且还对ViewModel都可以注入了哦。所以说它是专为Android开发而生。
关于注入的对象内存占用是否有优化的这一点,其实并没有文章或者官方的文档指出有内存优化这一点,仅我自己的测试来说,整个页面如果有多个注入对象和直接new对象相比,感觉注入的对象占用内存稍微少一点,不知道是不是测试的波动,我不理解,如有懂的大佬还望指点一下。
总结
总结为什么要使用Hilt。
- 偷懒;自动管理,多对象的自动注入,万一有修改不需要到尸山中到处趴。
- 单例;让对象拥有生命周期,无需我们自己手动单例创建,然后去手动注销。
- 解耦;不需要到处引入我一些不需要的对象,特别是组件化的项目,另一个组件只管注入,在我的组件中我只管引用。
我觉得这是我使用Hilt最吸引我的三个点,
所以说目前2022年了,依赖注入我推荐Hilt。关键使用简单,在Android的常用场景下我还做了一些 Demo, 总共就那么多固定的一些用法,之前我写过Demo覆盖Android开发大部分的使用场景, 有需要直接拿走即可,可以查看我之前的文章。
顺便说一句,这是国外程序员的必备面试技能,感觉相比国内的开发者老外的Android开发者特别喜欢使用Dagger2和Hilt,不少老项目都是用Dagger2的。
网友评论