Android 彻底组件化番外篇-gradle3.0.0

825 阅读7分钟
原文链接: mp.weixin.qq.com

作者 | 格竹子

地址 | http://www.jianshu.com/p/c7459b59dcd5

声明 | 本文是 格竹子 原创,已获授权发布,未经原作者允许请勿转载

前言

最近 Google 正式推出 AS3.0 版本,同时 gradle 插件也升级为3.0.0,目前各大开源库都在做 gradle3.0.0 的兼容,我也把得到开源的组件化方案 DDComponent 进行了升级,结论是:DDComponent在 gradle3.0 上是没有兼容问题的,可以直接使用。关于如何迁移到gradle3.0.0,请参见 官方迁移指南。

虽然没有兼容问题,但在升级的过程中也收获了意外之喜,那就是发现gradle3.0.0 对代码隔离的支持越来越好。为什么对“代码隔离”这么关注呢?大家可以回顾前两篇文章 Android 彻底组件化方案实践 和 Android 彻底组件化 demo 发布,在这两篇文章中提到的DDComponent 组件化方案,被我冠以“彻底”二字,虽然有些说大话,但主要是为了强调 DDComponent 与之前其他组件化方案的不同之处就在于,DDComponent 实现了组件之间的绝对隔离,不同组件之间在代码开发阶段是完全不可见的,是一种彻底解耦的思想。为了实现这种隔离,我人为在编译和运行期做了一次判断和区分,既在编译期间(开发期间)组件之间没有任何依赖关系,但在打包和运行时,再偷偷添加依赖。具体可以参见前两篇文章和 github 源码。

不得不说,当时这种实现是迫不得已,我本来想直接使用gradle提供的功能来做这种隔离,其实gradle也的确提供一个类似的功能,那就是apk依赖语法,其作用就是保证依赖库只在运行期间对外可见,但在编译期间是不可见的。按说这已经满足我的要求了,但是遇到了一个坑:在gradle2.+版本,apk依赖只能是jar,不能是aar,但是我们的组件因为含有各种资源,输出产物就是aar!所以最终选择了放弃apk这种语法。

而在最新的gradle3.0.0上,apk被替换为runtimeOnly语法,其作用还是一样的,但是我发现runtimeOnly可以添加aar依赖!这的确让我很兴奋,这不就是我梦寐以求的功能吗?有了这个尚方宝剑,组件化的方案就可以做的更薄了啊。于是我对在得到app上进行了实验,结论是:runtimeOnly的确可以解决一些问题,但是还不够。下面我从代码隔离、资源隔离和调试切换(单独和集成)三个方便仔细阐述,也顺便再讲一下DDComponent所能实现的功能。

代码隔离

在讲代码隔离之前,先大致看一下 gradle3.0.0 对添加依赖的语法变化。

首先 compile 被废弃了,而是分成了两个:implementation 和 api,其中 api 与之前的 compile 功能基本一致,不再赘述;implementation 就比较高级了,其作用就是,使用 implementation 添加的依赖不会再编译期间被其他组件引用到,但在运行期间是完全可见的。这也是一种代码隔离。举个例子,

组件A依赖lib1,既A implementation lib1
组件B依赖组件A,既B api A

在 gradle3.0.0 之前,B是完全可以引用到 lib1 里面的类的,但是现在B在编译期间就做不到了,只能在运行期可以。这种思想有点类似于“下属的下属不是你的下属”的思想。但是这种隔离在组件之间是不起作用的,在上面的例子中A的所有类对B还是完全可见的,也就是没有做任何隔离的。不过 implementation 的确是一种有效减少编译时间的方式,还是上面的例子,lib1发生了变化,现在只需要编译A就可以了,而在之前B有可能也使用到了lib1,所以需要同时编译B和A。按照官方建议,大部分情况下都应该使用 implementation 来进行添加依赖。

此外还有两种变化,原来的 apk 语法被 runtimeOnly 取代,provided 被 compileOnly 取代,其作用还是没变。上文也讲了,runtimeOnly 有个极大的改动就是可以支持aar了,但是 compileOnly 还是只能支持 jar!

先做一个小结,目前 gradle3.0.0 的四种语法的功能和代码隔离效果见下图:

从上图可以看出,在代码隔离效果上,runtimeOnly 的效果是最好的!但是就可以直接使用了吗,答案是否定的。

资源隔离

在前面的文章中,一直在强调代码隔离,其实组件之间的完全隔离还有一层就是资源隔离,否则还是容易造成组件之间的耦合。这个在文章的“单独调试”章节中提到了一句,就是每个组件都需要指定一个资源前缀 resourcePrefix,以避免集成后资源名冲突的问题。也就是说,一个彻底的组件化不仅要做到代码不能直接引用,资源也是不能引用的!

但是 runtimeOnly 目前还做到资源隔离,我在 DDComponent 的开源库上做了试验,app 通过 runtimeOnly 引用 sharecomponent 组件,虽然 sharecomponent 的代码是不可见了,但是资源还是可以被app直接使用的并能成功运行。

从这一点上看,直接替换成 runtimeOnly 是不行的,为了达到这种效果,目前还是需要像 DDComponent 一样,人为的加一层控制,所以从组件化方案的角度上看并没有变的更薄,不过幸好 DDComponent 已经很简单了,有一定的 gradle 基础的人可以比较容易的理解。

调试切换

除了上面说的资源隔离导致不能直接用 runtimeOnly 之外,还有一个使用上的问题需要解决,这也是 DDComponent 中 compbuild 插件提供的一个功能:自动切换单独调试和集成调试。在单独调试时,组件是一个application 工程,其输出产物是apk文件,而在集成调试时,被依赖的组件是一个 library 工程,其输出产物是 aar 文件。对于 runtimeOnly 来说,对 aar 和 jar 是支持的,但是不能支持 apk,所以如果想在单独调试和集成调试之间切换的话,需要人工修改 runalone 配置并修改 build.gradle 配置文件,然后还需要 sync 之后才能生效,这种修改是相当繁琐的。

在 DDComponent 中,这个问题的解决是通过“智能”识别当前要调试的组件来解决的,对于要调试的组件将其设置为 application 工程,而将其依赖的其他组件默默修改为 library 工程,这种修改是即时生效的,对开发者是完全透明的。开发者直接点击 AS 的 run 功能区就可以随意的调试任意组件。AS 的 run 功能区的图如下:

总结

综上所述,我们对 DDComponent 和 gradle3.0.0 做几点总结:

(1)升级到 gradle3.0.0 之后,可以继续使用 DDComponent,不需要专门做兼容

(2)gradle3.0.0 提供了 implementation 和 runtimeOnly 两种语法,它们都能实现一定程度的代码隔离效果,建议大家在今后优先使用

(3)implementation 和 runtimeOnly 目前还在资源隔离和调试切换上还能满足组件化的要求,所以还是需要使用 DDComponent 提供的完全隔离和随意切换功能。

在 DDComponent 的源码中我增加了 gradle3.0.0 分支,依赖语法做了相应的替换,欢迎大家继续支持“得到”app出品的组件化方案,源码地址:https://github.com/luojilab/DDComponentForAndroid

与之相关

Android 彻底组件化 demo 发布

Android 组件化 —— 路由设计最佳实践

END