Anvil - 使集成Daggar 2更容易的KCP - 2

398 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

扩展Anvil

每一个代码仓库有它自己的依赖注入模型, 其中某些代码结构会一而再再而三的重复. 这里Anvil前来拯救, 并且可以用自己的CodeGenerator来扩展编译器插件. 如果使用请查看compiler-api构件

Anvil的优势

在有许多应用目标的大型模块化工程中将Dagger modules添加进components是开销很大的. 你需要在创建新的Dagger module时知道把component定义在哪, 在设置新的应用时添加哪个module. 在新的module依赖添加进构建图谱之后这个任务包含很多的同步工作. 这个过程是冗长且乏味的. 有了Anvil, 只需要在构建图谱中添加一个依赖, 之后就立即可以测试构建了.

将构建图谱和Dagger的依赖图谱对齐带来很多一致性. 如果代码也处理编译的类路径上, 那么代码也将包含在Dagger依赖图谱中.

如果提供的对象绑定到了作用域, 那么Module将隐式地拥有这个作用域. 现在在没有任何绑定的情况下, module的作用域是很清晰的.

有了Anvil, 就不再需要任何复合Dagger module了, 复合Dagger module的唯一目的就是组合多个module以避免对多应用的重复设置. 复合module很容易成为毛团. 如果应用想要排除一个module, 那么它必须重复这种设置. 这个交叉图谱是令人痛苦且令人困惑的. 有了Dagger, 你便能够尽可能晚地决定哪些模块满足依赖关系,最好是在应用程序模块中. Anvil通过给包含的module生成代码使得这种途径容易了很多. 复合module是多余的. 你可以通过在应用程序模块中导入所需的模块来决定使用哪些绑定.

性能

Anvil是一个很方便的工具. 与Dagger相似, 与运行构建之前手写全部代码相比, Anvil并没有改善构建速度. 省的是开发者的时间.

Anvil的中值开销大约是 4%, 这通常意味着最多只有几百毫秒. 开销很微不足道的, 因为Kotlin代码是增量编译的, 如果代码没有发生变化, Kotlin编译任务会完全跳过. 有了Anvil这也不会改变.

benchmark.png

最重要的是, 如果启用了Dagger工厂生成, Anvil通过在很多module中取代Dagger注解处理器, 提供了真实的构建时间改善.

KCP

我们研究了其它的候选, 比如字节码插桩或者注解处理器, 它们是否是更好的选项, 但是最终决定不使用它们. 对于我们想要达到的目标, 字节码插桩在构建流程中运行的时机太晚了; 处于Dagger component的生成之后了. 尤其是在使用Kapt的时候, 注解处理器会非常非常慢. 即使KCP API并不稳定, 且包含bug, 我们依然决定写一个KCP.

限制

不支持Java

Anvil是一个KCP, 由此它并不支持Java. 尽管如此, 你依然可以在混合使用了Kotlin和Java的模块中, 为Kotlin类使用Anvil.

Kotlin增量编译破坏了编译器插件

有个bug影响了Anvil KCP:

Gradle插件为这个bug实现了一个变通方法, 所以你不应该会注意到这个bug. 只是负责效果是对于stub生成任务, 增量Kotlin编译被禁用了(它在Kapt之前将不会运行完整的编译). 此外, 如果模块包含Java代码的话, usePreciseJavaTracking这个标识也将禁用.

Hilt

Hilt是Google在Android上面如果进行依赖注入的固执己见. 它像Anvil一样, 通过@InstallIn为入口点和模块提供了相似的特性. 你如果用Hilt的话, 那么就不必使用Anvil了.

Hilt包含了许多其它的特性, 但也有一些限制. 对于我们而言, 将成千的模块的许多Dagger components的代码合并入Hilt是很不灵活的, 因为我们只想要自动合并modules和component接口的特性. 因为性能原因, 我们只对特定模块限制了Dagger注解处理器的使用. 有了Hilt, 我们将不能够对component interface强制使用这个需求了. Anvil的开发在Hilt宣布之前已经开始很久了, 而且内部版本也用于线上环境不小一会儿.