先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
正文
3. Dagger.android:代码生成组件树
dagger.android 是 Dagger 针对 Android 项目推出的子项目,核心思想是通过代码生成 subcomponent,降低 Andorid 项目中的模板代码。dagger.android 是独立于 Dagger 的库,工程中需要单独依赖:
//raw dagger2
implementation ‘com.google.dagger🗡2.x’
kapt ‘com.google.dagger:dagger-compiler:2.x’
//dagger.android
implementation ‘com.google.dagger:dagger-android:2.x’
implementation ‘com.google.dagger:dagger-android-support:2.x’
kapt ‘com.google.dagger:dagger-android-processor:2.x’
我们看一下引入 dagger.android 后的效果:
关键变化是新增了 @ContributesAndroidInjector
注解,它标记了一个返回值为 LoginActivity
的方法,其含义是编译期生成 LoginActivity
对应的子组件。因此我们无需再显示地通过 @Subcomponent
和 @Subcomponent.Factory
声明子组件了,SubcomponentsModule
中也无需添加 subcomponents
依赖。
3.1 @ContributesAndroidInjector 生成子组件
看一下 @ContributesAndroidInjector
生成的完整代码:
可以看到我们少写的代码,这里都生成了。LoginActivitySubcomponent
是子组件, SubcomponentsModule_ContributesLoginActivity
是用来安装子组件的 module。
3.2 DispatchingAndroidInjector 提供组件映射
特别值得一提的是代码中有一个 bindAndroidInjectorFactory
方法并携带了 @IntoMap
, @ClassKey
等若干注解,它们可以编译期 Dagger 构建依赖链条的过程中,向 DispatchingAndroidInjector
类填充一个 map,即其 injectorFactories
成员:
透过 map 的泛型定义不难推测,它是 Android class 与其对应子组件的工程的映射表,具体到前面 LoginActivity
的例子中,会填入 LoginActivity.class to LoginActivitySubcomponent.Factory
到 map 中
我们在 App 中声明 androidInjector
成员,并通过 Dagger 注入 DispatchingAndroidInjector
实例。App 通过 HasAndroidInjector
接口对外宣称自己持有一个 androidInjector
,可以为各个 Activity 提供注入。
如上,在 LoginActivity
中,我们不再需要通过父组件的工厂创建子组件,调用一个 AndroidInjection.inject
静态方法即可完成注入。静态方法内部会向上寻找 HasAndroidInjector
,然后通过映射表创建注入所需的子组件。
当然享受便利的同时,也要付出义务,LoginActivity
也需要实现 HasAndroidInjector
,并声明 androidInjector
,向下为它的 fragment 们提供注入。
3.3 dagger.android 的问题
dagger.andriid 主要做了下面两件事,帮我们减少了模板代码 :
- 通过 @ContributesAndroidInjector 生成 subcomponent 及其 factory,省去了我们显示地定义子组件,父组件也不需要再声明 Subcomponent.Factory
- 让 Android 各层级对象持有 AndroidInjector,通过静态方法完成对低层级对象的注入,省去了显示地创建子组件完成注入
dagger.android 虽然做出上述改善,但是代价是引入了一些新的模板代码 :
- 需要配置 @ContributesAndroidInjector
- Android 组件需要实现 HasAndroidInjector 接口,并注入 AndroidInjector 成员
- 需要手动调用 AndroidInjection.inject
4. Hilt:预定义组件树
dagger.android 没有存在模板代码,所以诞生了 Hilt,后者的思想是通过 “预定义” 的方式彻底消灭模板代码。
plugins {
id ‘kotlin-kapt’
id ‘com.google.dagger.hilt.android’
}
dependencies {
implementation “com.google.dagger:hilt-android:2.x”
kapt “com.google.dagger:hilt-compiler:2.x”
}
4.1 预定义 Component
相对于 dagger.android 帮我们生成 LoginActivitySubComponent
, Hilt 中索性不允许自定义的 subcomponent,提供了预定义的 ActivityComponent
作为所有 activity 共享的提供注入的组件。而 LoginActivityModule
等原本安装到 LoginActivitySubComponent
的依赖,通过 @installIn
注解安装到 ActivityComponent
中。
ActivityComponent
是个 interface,编译期生成实现类 ActivityC
:
modules 中可以看到各 activity 依赖的 XXXActivityModule
都被 instllIn
中 ActivityC
中,ActivityC
可以为所有 activity 提供注入。
FragmentCBuilderModule
和 ViewCBuilderModule
用来安装 Hilt 另外两个预定义组件 FragmentComponent
和 ViewComponent
,Hilt 为 Android 中的关键概念都提供了对应的预定义组件,且将它们建立树行关系。
4.2 预定义 Inject
dagger.android 通过提供静态方法注入降低了 inject 的成本。而在 Hilt 中,inject 的成本趋近于零,只需要在 activity 等 Android 组件添加 @AndroidEntryPoint
注解,其他什么都不用做。
前面看到 ActivityC
实现了 XXXActivity_GeneratedInjector
接口,这些接口就是 @AndroidEntryPoint
的产物
LoginActivity_GeneratedInjector
提供了面向 LoginActivity
的 inject()
方法,并通过 @EntryPoint
安装到 ActivityComponent
中,这样编译后 ActivityC
就具备了 injectLoginActivity
的能力。
@EntryPoint 是 Hilt 的重要注解,因为我们没法在 Hilt 的预定义 Component 中添加 inject 方法,所以当我们希望 Hilt 为自定义类提供注入时,可以自定义 inject 接口,通过
@EntryPoint
安装到 Hilt 的预定义组件中。@AndroidEntryPoint
只不过是针对 Android 类提前生成了@EntryPoint
代码。
那么 LoginActivity
是什么时候调用 ActivityC
的 injectLoginActivity
完成自身注入的呢?Hilt 会为 LoginActivity
生成一个 activity 的派生类,它在合适的时间点调用 inject()
,内部会调用 ActivityC#injectLoginActivity
而 Hilt 通过 Transform + ASM,让编译后 LoginActivity
继承了 Hilt_LoginActivity
,这样就可以在不写任何代码的情况下,让 LoginActivity
基于 Hilt 的 ActivityC
完成注入,即所谓的 “预定义 inject”。
4.3 预定义 @Scope
Dagger 在 Android 中使用时,往往需要需要自定义作用域注解表明不同 Android 类的生命周期。Hilt 伴随着预定义组件,也提供了与之对应的预定义作用域注解
例如,添加了 @ActivityScoped
注解,表示 provides
的对象在 ActivityComponent
范围内以单例存在。
Hilt 为所有的关键的 Android 类都提供了预定义组件和相对应的预定义作用域注解,所以也可以说 Hilt 对整棵组件树进行了预定义:
5. 渐进式迁移
通过前面介绍,我们能感受到 Hilt 对 Dagger 的 Boilerplate 问题进行了比较彻底的改进,建议大家尽快升级到 Hilt。从组件树的视角来理解 Dagger 与 Hilt 的区别,可以帮助我们完成渐进式的升级。
最安全的升级过程就是从沿着组件树的树干,按照 Application -> Activity -> Fragment -> ...
的顺序,将 Dagger 的自定义组件合并到 Hilt 的预定义组件,最终实现依赖注入完全托管到 Hilt 树。
5.1 将 Dagger 组件合并到 Hilt
我们以 ApplicationComponent
为例看一下,看一下 Dagger 中自定义 ApplicationComponent
如何合并到 Hilt 的预定义的 SingletonComponent
。
合并后的代码如下所示,核心是 @EntryPoint
的使用:
@EntryPoint
让SingletonC
实现了ApplicationComponent
接口,代码中其他依赖ApplicationComponent
的地方可以无缝切换到SingletonComponent
,两棵树在根节点完成合并。- 新定义一个 module,通过
includes
将原本ApplicationComponent
的依赖打包安装到SingletonComponent
。
如上,在 App 中添加 @HiltAndroidEntryPoint
注解,Hilt 可以为 App 提供注入服务。但是 App 的 component 成员不能立即删除,可能还有其他代码在引用它。但是在根节点合并后,我们可以通过 EntryPointAccessors
从 Hilt 获取 ApplicationComponent
的实现。
其他层级的组件,例如 ActivityComponent
, FragmemtComponent
等,也可以仿照 ApplicationComponent
向 Hilt 做合并。如果你使用的是 dagger.android,则不再需要 @ContributesAndroidInjector
生成预定义组件了,可以删除相关代码,将依赖的 module 安装到 Hilt 的对应组件,如下:
5.2 清理 Dagger 残留代码
当我们将组件树上的所有组件都合并到 Hilt,Android 类都转向通过 Hilt 获取依赖注入,所以 Dagger 或者 dagger.android 相关的代码都可以清理掉了
如上,以 App 为例,所以 Dagger 或者 dagger.android 相关的注入代码都可以删除了,代码清爽多了。
6. 预定义组件的问题
Hilt 的预定义组件在降低代码复杂度的同时,也丧失了自定义组件的灵活性,我们来看两个常见问题及应对方案
6.1 区分具体 activity 类型
Dagger 依赖图中可能有对当前具体 activity 类型的依赖。通常我们像下面这样,在创建 LoginActivityComponent
时为 Dagger 传入 activity 实例。
@ActivityScope
@Subcomponent
interface LoginActivityComponent {
@Subcomponent.Factory
interface Factory {
fun create(@BindsInstance activity: LoginActivity): LoginActivityComponent
}
}
Hilt 中没有机会创建自定义组件,该如何提供不同类型的 activity 依赖呢。
预定义组件 ActivityComponent
中默认提供了当前 activity 的依赖,但是不区分具体类型,我们可以通过不同类型的 module 强转 activity 为具体类型后提供出去,代码如下:
@InstallIn(ActivityComponent::class)
@Module
class LoginActivityModule {
@Provides
fun providesLoginActivity(activity: Activity): LoginActivity> =
activity as? LoginActivity?
}
但是必须说一句,对具体 activity 类型的依赖并非一个好设计,这意味着 activity 可能违反了单一职责的设计原则。
6.2 根据目标 activity 提供同一接口的不同实现
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
可能违反了单一职责的设计原则。
6.2 根据目标 activity 提供同一接口的不同实现
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-6x5ZlKYJ-1713172437194)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!