前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从 Dagger 到 Hilt,谷歌为何执着于让我们用依赖注入?

从 Dagger 到 Hilt,谷歌为何执着于让我们用依赖注入?

作者头像
扔物线
修改2020-07-31 17:31:45
1.1K0
修改2020-07-31 17:31:45
举报
文章被收录于专栏:HenCoderHenCoderHenCoder

开始

说到依赖注入,做 Android 的人都会想到一个库:Dagger;说到 Dagger,大家的反应普遍是一套三连:牛逼、高端、我才不用。

又牛逼又高端,为什么不用?因为太难了。是吧?又难学又难用。大多数的人在学习 Dagger 的路上就被直接劝退了,剩下的这一小撮人最终排除万难,学会并且用上了 Dagger,但多半都是用着用着就掉进了自己亲手用 Dagger 搭建的迷宫里,怎么也绕不清楚,而且越陷越深,就这么成年累月地被它折磨。

有人可能会说:难用就别用呗?拆出来啊。

拆?哼哼。你对 Dagger 一无所知。

而就在上个月,Android 团队又在 Jetpack 里面又增加了一个新的依赖注入库:Hilt。这个 Hilt 是专门针对于 Android 平台的依赖注入库,它是基于 Dagger 的。

啊?基于……Dagger?这次到底是真正的神器到来,还是又一个大坑?

依赖注入是什么

Dagger 的名字取自有向无环图 DAG (directed acyclic graph):

因为程序里的依赖关系拼接起来就是一个或者多个有向无环图:

DAG-er,Dagger,取了个谐音,Dagger 是匕首的意思。而这次的 Hilt 是刀柄的意思,匕首很难用是吧?来,给你个柄。

说得很好听,到底有没有那么好用啊?这是个复杂的问题,且听我慢慢道来~

依赖注入有什么用

Hilt 好不好用,我们先来看看它是个什么。它是个用注解来进行配置的依赖注入库。注解是它的写法,首先它是个依赖注入库,对吧?什么是依赖注入?一个类里有两个变量,这两个变量就是它的依赖:

要初始化一个依赖,有两种方法:第一,你这个类自己初始化:

第二,让外部帮你初始化。

其中这第二种,让外部帮你初始化你的依赖,就叫依赖注入。关键在于初始化是谁做的,至于最后一步是你把结果拿过来,还是说你连拿都不用拿,最后一步的赋值工作也让外部来帮你做了,这都不重要,只要初始化工作是外部做的,就都叫依赖注入。

所以 Factory 的使用是依赖注入吗?

是的。

Builder?

也是。

带参数的构造函数?

也是!

这些都属于由外部来提供依赖的初始化,所以都是依赖注入,并不是非要像 Dagger 那样使用注解的像魔法一样的才叫依赖注入。也就是说,其实我们每个人都已经在使用依赖注入了。虽然很多人在面对 Dagger 的时候会问「依赖注入到底有什么用」,但其实 Dagger 并不是提供了依赖注入的能力,而是为依赖注入提供了一种更简单的方式。依赖注入本来就是有用的,这个问题不想明白,不管是 Dagger 还是现在的 Hilt,你都用不好。

Dagger 让我们可以用注解的方式来配置依赖关系,让依赖注入变得更方便。不过由于功能复杂,导致它的上手非常困难;再加上刚才我说的,很多人对于依赖注入的作用以及 Dagger 的定位都没搞清楚,这两个原因加起来,就导致很多人还没学会 Dagger 就把它弃了,让 Dagger 成为 Android 史上最受冷落的优质库。这样的结果不论是对 Dagger 还是对我们,都是很可惜的。

而 Hilt 的出现,就直接解决了 Dagger 太复杂的这个问题。

Hilt 怎么帮助我们进行依赖注入

Hilt 是 Google 专门针对 Android 平台做的一个依赖注入库。它不是从里到外全新开发的,而是基于 Dagger 做的,它的下层还是 Dagger。

为什么不直接去优化改进 Dagger,而要基于它做一个新库呢?因为 Hilt 做的事其实也并不是对 Dagger 进行优化,而是场景化:针对 Android 开发制定了一系列的规则,通过这些规则大大简化了这套工具的使用。例如在 Dagger 里,你要对某个类的依赖进行注入,你需要手动获取依赖图和执行注入依赖操作:

而在 Hilt 里,注入会自动完成:

因为 Hilt 会自动找到 Android 的系统组件里面那些最佳的初始化位置——比如 Activity 的 onCreate() ——然后在这些位置注入依赖。所以,为什么不是去优化 Dagger,而是做了个新库?因为 Hilt 本身并不是一种优化,而是场景化,或者说,它是一种针对场景的优化。总之,它是不通用的,只能给 Android 用,所以不能放在 Dagger 里。

有点明白了吧?

那它具体怎么用呢?大概是这样的:

我们程序里有些对象是全局共享的,比如线程池,或者 Retrofit 对象,这种东西我们通常会把它放在 Application 对象里,或者做成单例的:

而如果用 Hilt,你也可以把它做成自动注入的依赖:

还有些对象是局部共享的,比如某个 Activity 会把一些显示用的数据共享给它内部的一些 View 和 Fragment。这一类情况我们的做法通常是获取外部 Activity 对象然后强转,再去拿它内部的对象:

而如果用 Hilt,你可以把这个对象直接声明出来,让它自动注入:

这不只是一个「美观」的差别,依赖注入可以让你的程序更加灵活,比如如果你的 View 可以在多个不同的 Activity 里显示,那你在 View 里面要怎么强转?你要转成谁?

很麻烦,是吧?而如果用依赖注入,这些就都是自动的。

除了共享的对象,不共享的也可以用依赖注入的方式来进行初始化,因为依赖注入的作用除了对共享对象提供一致性支持,也可以让我们在创建任何对象的时候省一些思考和力气:

@Inject newUser: User

总之,如果一个组件可能会被被共享,或者不会被共享但可能会在多处使用,你都可以使用 Hilt 来把它配置成依赖注入的加载方式。

加载的方式可以选择直接调用构造函数:

或者指定子类或实现类:

或者干脆给出具体的代码:

加载的作用域可以选择默认的每次都初始化,也可以设置成全局单例的:

也可以设置成针对任何 Activity、Fragment、View 或者 ViewModel 的局部共享:

简单又强大,好用又灵活。具体的写法你可以去看文档,或者过段时间我会有一次公开课,到时候也会提前通知大家。

到这里有的人可能会分个叉可能会想:诶 ButterKnife 或者现在 Jetpack 推出的 ViewBinding 它们提供的功能,Hilt 提供了吗?因为如果提供了,我在用了 Hilt 之后,不就可以把 ButterKnife 和 ViewBinding 扔掉了?

不好意思,Hilt 不提供它们的功能。Hilt 和 Dagger 虽然用法和 ButterKnife 很像,都是给变量加注解,然后变量会自动赋值,但它们的功能定位是不一样的:Hilt 和 Dagger 是做依赖注入的,而 ButterKnife 和 ViewBinding 是做视图绑定的。

这可不是个文字游戏,依赖注入和视图绑定是有本质区别的:依赖注入是由外部对对象进行初始化,也就是所谓的控制翻转;而视图绑定是让变量去指向一个已经有了的 View,它的依赖依然是由依赖持有者自己决定的,这是一个本质的区别。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 扔物线 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开始
  • 依赖注入是什么
  • 依赖注入有什么用
  • Hilt 怎么帮助我们进行依赖注入
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档