Hilt 扩展 | MAD Skills

Hilt 扩展 | MAD Skills
www.zeeklog.com  - Hilt 扩展 | MAD Skills

本文是  中有关  的第四篇文章!在本文中,我们将探讨如何编写自定义的 Hilt 扩展。如果您需了解本系列前三篇文章,请查阅:

如果您更喜欢通过视频了解此内容,可以在此处查看:

△ Hilt 扩展——MAD Skills

案例: WorkManager 扩展

Hilt 扩展是一个生成代码的库,常通过注解处理器实现。生成的代码作为构成 Hilt 依赖项注入关系图的模块或入口点。

的集成库就是一个扩展的例子。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 中。

www.zeeklog.com  - Hilt 扩展 | MAD Skills

由于 Hilt 具有聚合能力,任何通过添加 @InstallIn 注解生成 @Module 及 @EntryPoint 的工具都可以被 Hilt 发现,并在编译期成为 Hilt DI 图中的一部分。这使得扩展可以轻松地以插件形式集成到 Hilt,无需开发者处理任何额外工作。

注解处理器

生成代码的常规途径是使用注解处理器。源文件转换为 class 文件之前,注解处理器会在编译器中运行。当资源带有处理器所声明的已支持的注解时,处理器会进行处理。处理器可以生成进一步需要被处理的方法,因此编译器会不断循环运行注解处理器,直到没有新的内容产生。一旦所有的环节都完成,编译器才会将源文件转换为 class 文件。

www.zeeklog.com  - Hilt 扩展 | MAD Skills

△ 注解处理示意图

由于循环机制,处理器可以相互作用。这非常重要,因为这使得 Hilt 的注解处理器可以处理由其他处理器生成的 @Module 或 @EntryPoint 类。这也意味着您的扩展也可以建立在其他人编写的扩展之上!

根据带有 @HiltWorker 注解的类生成代码,同时验证注解用法并使用  等库生成代码。

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。如果有多种类型需要被成员注入,那么自动创建入口点的扩展会很有用。例如,需要通过  发现服务实现的库负责实例化发现的服务。为了将依赖项注入到服务实现中,必须创建一个 @EntryPoint。通过使用 Hilt 扩展,可以使用在实现类上添加注解完成自动生成入口点。扩展可以进一步生成代码以使用入口点,例如由服务实现扩展的基类。这类似于 @AndroidEntryPoint 为 Activity 创建 @EntryPoint,并创建使用生成的入口点在 Activity 中执行成员注入的基类。

镜像绑定

有时需要使用不同的限定符来镜像或重新声明绑定。当存在自定义组件时,这可能更常见。为了避免丢失重新声明的绑定,可以创建 Hilt 扩展以自动生成其他镜像绑定的模块。例如,考虑包含不同依赖项实现的应用中 “付费” 和 “免费” 订阅的情况。然后,每一层都有两个不同的自定义组件,这样您就可以确定依赖关系的作用域。当添加一个通用的未限定作用域的绑定时,定义绑定的模块可以在其 @InstallIn 中包含两个组件,也可以加载在父组件中,通常是单例组件。但是当绑定被限定作用域时,模块必须被复制,因为需要不同的限定符。实现一个扩展就可以生成两个模块,可以避免样板代码并确保不会遗漏通用绑定。

总结

Hilt 的扩展可以进一步增强代码库中的依赖项注入能力,因为它们可以实现与 Hilt 尚不支持的其他库集成。总而言之,扩展通常由两部分组成,包含扩展注解的运行时部分,以及生成 @Module 或 @EntryPoint 的代码生成器 (通常是注解处理器)。扩展的运行时部分可能有额外的辅助类,这些辅助类使用声明在生成的模块或入口点中绑定。代码生成器还可能生成与扩展相关的附加代码,它们无需专门生成模块和入口点。

扩展必须使用两个注解才能与 Hilt 正确交互:

  • @GeneratesRootInput 添加在扩展注解上。
  • @OriginatingElement 由扩展添加在生成的模块或入口点上。

最后,您可以查看  项目,这是一个简单扩展的示例,它展示了本文中提到的概念。

以上便是  系列关于 Hilt 的全部内容,如需观看视频全集,请移步到 。感谢阅读本文!

欢迎您  向我们提交反馈,或分享您喜欢的内容、发现的问题。您的反馈对我们非常重要,感谢您的支持!

Read more

Android学习总结(6)——Android日常开发总结的技术经验60条

Android学习总结(6)——Android日常开发总结的技术经验60条

1. 全部Activity可继承自BaseActivity,便于统一风格与处理公共事件,构建对话框统一构建器的建立,万一 需要整体变动,一处修改到处有效。   2.数据库表段字段常量和SQL逻辑分离,更清晰,建议使用Lite系列框架LiteOrm库,超级清晰且重心可以放在业务上不用关心数据库细节。   3. 全局变量放全局类中,模块私有放自己的管理类中,让常量清晰且集中.   4. 不要相信庞大的管理类的东西会带来什么好处,可能是一场灾难,而要时刻注意单一职责原则,一个类专心做好 一件事情更为清晰。   5. 如果数据没有必要加载,数据请务必延迟初始化,谨记为用户节省内存,总不会有坏处。   6. 异常抛出,在合适的位置处理或者集中处理,不要搞的到处是catch,混乱且性能低,尽量不要在循环体中捕获异常,以提升性能。   7. 地址引用链长时(3个以上指向)小心内存泄漏,和警惕堆栈地址指向,典型的易发事件是:数据更新了,ListView视图却没有刷新,这时Adapter很可能指向并的并不是你更新的数据容器地址(一般为List)。   8.信息同步:不管是数据库还是网网络操作,新插入的数

By Ne0inhk
成为Java高手的25个学习要点

成为Java高手的25个学习要点

1. 你需要精通面向对象分析与设计(OOA/OOD)、涉及模式(GOF,J2EEDP)以及综合模式。你应该了解UML,尤其是class、object、interaction以及statediagrams。 2. 你需要学习Java语言的基础知识以及它的核心类库(collections、serialization、streams、networking、multithreading、reflection、event、handling、NIO、localization以及其他)。 3. 你应该了解JVM、classloaders、classreflect以及垃圾回收的基本工作机制等。你应该有能力反编译一个类文件并且明白一些基本的汇编指令。 4. 如果你将要写客户端程序,你需要学习Web的小应用程序(applet),必需掌握GUI设计的思想和方法,以及桌面程序的SWING、AWT、SWT。你还应该对UI部件的JavaBEAN组件模式有所了解。JavaBEANS也被应用在JSP中以把业务逻辑从表现层中分离出来。 5. 你需要学习Java 数据库 技术,并且会使用至少一种persisten

By Ne0inhk
Docker学习总结(20)——Docker 容器实践精华问答集锦

Docker学习总结(20)——Docker 容器实践精华问答集锦

问题一:请问如何做好容器的安全性管理?跨主机容器间通信如何来管理(比如:ip 分配,划vlan 等)?k8s对于容器的监控和集群管理,该如何来做? 精灵云:关于安全,容器自带一些安全技术比如Capability用于限制容器所拥有的能力,也就是执行某些系统操作的权限,也可以根据需要对容器的能力进行增减。Namespace为每个容器提供隔离的系统运行环境,包括pid, network, uts, ipc, mount。Cgroup主要用于对容器所使用的资源进行限制,包括cpu,memory。技术措施上,对权限和资源进行限制,用cap-drop的方式来削减能力,利用cgroup来为容器添加cpu,和memory限制。 跨主机的通信,精灵云基于Calico网络上,做了二次开发,可以实现容器跨主机间的IP直接访问,并支持同一集群的多子网创建。 关于容器的监控和集群管理。精灵云提供专门的配置接口和监控界面,让用户可以很方便的了解容器的资源使用情况,日志的实时收集和分析。通过DashBoard可以一目了然的了解集群中所有主机的资源消耗情况。也可以周期性地对容器健康状态进行监控。 问题二:请问现在

By Ne0inhk
Css学习总结(4)——CSS选择器总结

Css学习总结(4)——CSS选择器总结

1:通用选择器 * {   margin:0;   padding:0;  } *选择器是选择页面上的全部元素,上面的代码作用是把全部元素的margin和padding设为0,最基本的清除浏览器默认样式的方法。 *选择器也可以应用到子选择器中,例如下面的代码: #container * {   border:1px solid black;  } 这样ID为container 的所有子标签元素都被选中了,并且设置了border。 2:id选择器 #container {    width: 960px;    margin: auto; } id 选择器是很严格的并且你没办法去复用它。使用的时候大家还是得相当小心的。需要问自己一下:我是不是必须要给这个元素来赋值个id来定位它呢? 3 .class:类选择器 .error {   color: red; } 这是个 class 选择器。它跟 id 选择器不同的是,它可以定位多个元素。当你想对多个元素进行样式修饰的时候就可以使用 class 。当你要对某个特定的元素进行修饰那就是用 id 来定位它。 4

By Ne0inhk