共计 3432 个字符,预计需要花费 9 分钟才能阅读完成。
本文是 MAD Skills 系列 中无关 Hilt 的第四篇文章!在本文中,咱们将探讨如何编写自定义的 Hilt 扩大。如果您需理解本系列前三篇文章,请查阅:
- Hilt 介绍
- Hilt 测试最佳实际
- Hilt 工作原理
如果您更喜爱通过视频理解此内容,能够点击 此处 查看。
案例: WorkManager 扩大
Hilt 扩大是一个生成代码的库,常通过注解处理器实现。生成的代码作为形成 Hilt 依赖项注入关系图的模块或入口点。
Jetpack 中 WorkManager 的集成库就是一个扩大的例子。WorkManager 扩大帮忙咱们缩小向 worker 提供依赖项时所需的模板代码及配置。该库由两局部组成,别离为 androidx.hilt:hilt-work 和 androidx.hilt:hilt-compiler。第一局部蕴含 HiltWorker 注解以及一些运行时的辅助类,第二局部是一个注解处理器,依据第一局部中注解提供的信息生成模块。
扩大的应用非常简单,仅需在您的 worker 上增加 @HiltWorker 注解:
@HiltWorker
public class ExampleWorker extends Worker {// ...}
扩大编译器会生成一个增加了 @Module 注解的类:
@Generated("androidx.hilt.AndroidXHiltProcessor")
@Module
@InstallIn(SingletonComponent.class)
@OriginatingElement(topLevelClass = ExampleWorker.class)
public interface ExampleWorker_HiltModule {
@Binds
@IntoMap
@StringKey("my.app.ExmapleWorker")
WorkerAssistedFactory<? extends ListenableWorker> bind(ExampleWorker_AssistedFactory factory);
}
该模块为 worker 定义了一个能够拜访 HiltWorkerFactory 的绑定。而后,配置 WorkerManager 应用该 factory,从而使 worker 的依赖项注入可用。
Hilt 聚合
启用扩大的一个要害机制是 Hilt 可能从类门路中发现模块和入口点。这被称为聚合,因为模块和入口点被聚合到带有 @HiltAndroidApp 注解的 Application 中。
因为 Hilt 具备聚合能力,任何通过增加 @InstallIn 注解生成 @Module 及 @EntryPoint 的工具都能够被 Hilt 发现,并在编译期成为 Hilt DI 图中的一部分。这使得扩大能够轻松地以插件模式集成到 Hilt,无需开发者解决任何额定工作。
注解处理器
生成代码的惯例路径是应用注解处理器。源文件转换为 class 文件之前,注解处理器会在编译器中运行。当资源带有处理器所申明的已反对的注解时,处理器会进行解决。处理器能够生成进一步须要被解决的办法,因而编译器会一直循环运行注解处理器,直到没有新的内容产生。一旦所有的环节都实现,编译器才会将源文件转换为 class 文件。
△ 注解解决示意图
因为循环机制,处理器能够相互作用。这十分重要,因为这使得 Hilt 的注解处理器能够解决由其余处理器生成的 @Module 或 @EntryPoint 类。这也意味着您的扩大也能够建设在其他人编写的扩大之上!
WorkManager extension processor 依据带有 @HiltWorker 注解的类生成代码,同时验证注解用法并应用 JavaPoet 等库生成代码。
Hilt 扩大注解
Hilt API 中有两个重要的注解: @GeneratesRootInput 和 @OriginatingElement。扩大应该应用这些注解能力与 Hilt 正确集成。
扩大应该应用 @GeneratesRootInput 来启用代码生成的注解。这让 Hilt 注解处理器晓得它应该在生成组件之前实现扩大注解处理器的工作。例如,@HiltWorker 注解自身是被 @GeneratesRootInput 注解润饰的:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@GeneratesRootInput
public @interface HiltWorker {}
所生成的带有 @Module、@EntryPoint 以及 @InstallIn 注解的类都须要增加 @OriginatingElement 注解,该注解的输出参数是触发模块或入口点生成的顶层类。这就是 Hilt 判断生成的模块和入口点是否在本地测试的根据。例如,在 Hilt 测试中定义了一个增加 @HiltWorker 注解的外部类,模块的初始元素就是测试值。
测试案例如下:
@HiltAndroidTest
class SampleTest {
@HiltWorker
class TestWorker extends Worker {// …}
}
生成的模块蕴含 @OriginatingElement 注解:
@Module
@InstallIn(SingletonComponent.class)
@OriginatingElement(topLevelClass = SampleTest.class)
public interface SampleTest_TestWorker__HiltModule {// …}
心得
Hilt 扩大反对多种可能性,以下是创立扩大的一些心得:
我的项目中的通用模式
如果您的我的项目中有创立模块或入口点的通用模式,那么它们很大概率能够通过应用 Hilt 扩大实现自动化。举个例子,如果每一个实现特定接口的类都必须创立一个具备多绑定的模块,那么能够创立一个扩大,只需在实现类上增加注解即可生成多重绑定模块。
反对非标准成员注入
对于那些 Framework 中曾经反对带有实例化能力的成员注入类型,咱们须要创立一个 @EntryPoint。如果有多种类型须要被成员注入,那么主动创立入口点的扩大会很有用。例如,须要通过 ServiceLoader 发现服务实现的库负责实例化发现的服务。为了将依赖项注入到服务实现中,必须创立一个 @EntryPoint。通过应用 Hilt 扩大,能够应用在实现类上增加注解实现主动生成入口点。扩大能够进一步生成代码以应用入口点,例如由服务实现扩大的基类。这相似于 @AndroidEntryPoint 为 Activity 创立 @EntryPoint,并创立应用生成的入口点在 Activity 中执行成员注入的基类。
镜像绑定
有时须要应用不同的限定符来镜像或从新申明绑定。当存在自定义组件时,这可能更常见。为了防止失落从新申明的绑定,能够创立 Hilt 扩大以主动生成其余镜像绑定的模块。例如,思考蕴含不同依赖项实现的利用中 “ 付费 ” 和 “ 收费 ” 订阅的状况。而后,每一层都有两个不同的自定义组件,这样您就能够确定依赖关系的作用域。当增加一个通用的未限定作用域的绑定时,定义绑定的模块能够在其 @InstallIn 中蕴含两个组件,也能够加载在父组件中,通常是单例组件。然而当绑定被限定作用域时,模块必须被复制,因为须要不同的限定符。实现一个扩大就能够生成两个模块,能够防止样板代码并确保不会脱漏通用绑定。
总结
Hilt 的扩大能够进一步加强代码库中的依赖项注入能力,因为它们能够实现与 Hilt 尚不反对的其余库集成。总而言之,扩大通常由两局部组成,蕴含扩大注解的运行时局部,以及生成 @Module 或 @EntryPoint 的代码生成器 (通常是注解处理器)。扩大的运行时局部可能有额定的辅助类,这些辅助类应用申明在生成的模块或入口点中绑定。代码生成器还可能生成与扩大相干的附加代码,它们无需专门生成模块和入口点。
扩大必须应用两个注解能力与 Hilt 正确交互:
- @GeneratesRootInput 增加在扩大注解上。
- @OriginatingElement 由扩大增加在生成的模块或入口点上。
最初,您能够查看 hilt-install-binding 我的项目,这是一个简略扩大的示例,它展现了本文中提到的概念。
以上便是 MAD Skills 系列对于 Hilt 的全部内容,如需观看视频选集,请移步到 Hilt – MAD Skills 播放列表。感激浏览本文!
欢迎您 点击这里 向咱们提交反馈,或分享您喜爱的内容、发现的问题。您的反馈对咱们十分重要,感谢您的反对!