注册

不就new个对象的事,为什么要把简单的问题复杂化?为什么要使用Hilt依赖注入?看完就知道!

为什么要使用Hilt依赖注入


之前有写过一些文章 Hilt与Koin的对比,和 Android开发中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。



  1. 偷懒;自动管理,多对象的自动注入,万一有修改不需要到尸山中到处趴。
  2. 单例;让对象拥有生命周期,无需我们自己手动单例创建,然后去手动注销。
  3. 解耦;不需要到处引入我一些不需要的对象,特别是组件化的项目,另一个组件只管注入,在我的组件中我只管引用。

我觉得这是我使用Hilt最吸引我的三个点,


所以说目前2022年了,依赖注入我推荐Hilt。关键使用简单,在Android的常用场景下我还做了一些 Demo, 总共就那么多固定的一些用法,之前我写过Demo覆盖Android开发大部分的使用场景, 有需要直接拿走即可,可以查看我之前的文章。


顺便说一句,这是国外程序员的必备面试技能,感觉相比国内的开发者老外的Android开发者特别喜欢使用Dagger2和Hilt,不少老项目都是用Dagger2的。


好了,本期内容如讲的不到位或错漏的地方,希望同学们可以指出交流。


如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。


Ok,这一期就此完结。



作者:newki
链接:https://juejin.cn/post/7129673928460468254
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册