Gradle Transform 初探

dim.red
环境: gradle 4.1 , Android Plugin 2.3.0 , Android Plugin 3.0.0

0x00 背景

去年的时候写个 Android Plugin Transform 初探 . 现在我们接着之前的脚步来学习 Gradle 下的 Transform . 同时熟悉一下 Android Plugin 在该规则下的应用.

0x01 历史

Android Plugin 是在1.5.0-beta1 版本加入的. 1.5.0 的 Release 是在2015 年的 11 月.
Gradle 的 Transform api 是在 3.5 版本引入的. 3.5.0 的 Release 是在 2017 年的 4 月.
相似的命名, 相似的功能,

0x02 使用

Gradle 中 Configuration 代表一组依赖关系.而 Transform 是作用在依赖的产物上, 将产物 A 根据一定规则转换成产物 B.
依赖的产物默认有一个属性ArtifactAttributes.ARTIFACT_FORMAT, 远程依赖默认为文件的后缀名. 本地依赖的情况比较复杂,具体看应用的插件.

先看一个比较全的应用

Lib 工程提供 Configuration. 供主工程消费.

1 Lib 工程

Configuration apLib = project.configurations.create("apLib");
        apLib.setDescription("ap  elements " );
        // 能被其他 project 的消费
        apLib.setCanBeConsumed(true);
        // 设置当前的 Configuration 的属性. 供请求 Configuration 的属性匹配
        apLib.getAttributes().attribute(Attribute.of("Type", String.class), "ap")
        // 声明一个variants apVar , 设置 apVar 属性为 ap, 同时为apVar 设置最终产物 /lib/demo.ap
        apLib.getOutgoing().variants(new Action<NamedDomainObjectContainer<ConfigurationVariant>>() {
            @Override
            void execute(NamedDomainObjectContainer<ConfigurationVariant> configurationVariants) {

                configurationVariants.create("apVar", { variant ->
                    variant.artifact(new File("/lib/demo.ap"), { artifact ->
                        artifact.setType("ap");
                    })
                    variant.getAttributes().attribute(Attribute.of("artifactType", String.class), "ap")
                })
            }
        });

这里声明了一个 apLib 的 Configuration . 并且为这个apLib 设置了一些属性,这些属性将在后面查找提供作用, 同时为 apLib 设置它的产物 . 也可以为这个 artifact 设置依赖任务 (artifact.builtBy(Task)) , 获取这个产物会先执行依赖任务.

2 主工程

定义一个 apC . 获取 apC 依赖上所有 ap 类型的文件.

Configuration apC = project.configurations.create("apC");
//主动依赖 Lib 工程
project.getDependencies().add("apC", project.project(':lib'));

//定义 apC 属性,  主要用来匹配 lib 生成的 apLib 的属性
apC.getAttributes().attribute(Attribute.of("Type", String.class), "ap")
apC.getIncoming().artifactView({
   config ->
         config.attributes {
            // 定义属性, 用于匹配 apLib apVar 的属性
             container -> container.attribute(Attribute.of("artifactType", String.class), "ap")
         }
}).getArtifacts().getArtifactFiles().getFiles(); //  /lib/demo.ap

数据流向: 根据 apC 的属性匹配到 lib 工程的 apLib 的属性. 根据 container 的属性匹配到了 apVar 的属性. 获取到产物.
属性的匹配默认是调用两个值的 equals 方法. 当然你可以自己定义一个匹配策略和一个解决匹配冲突的的策略.

 AttributeMatchingStrategy attributeMatchingStrategy = project.getDependencies().getAttributesSchema().attribute(Attribute.of("artifactType", String.class)); // 获取规则
 attributeMatchingStrategy.compatibilityRules.add(TypeCompatibilityRules.class); // 定义新的匹配策
 attributeMatchingStrategy.disambiguationRules.add(TypeDisambiguationRules.class); // 定义解决冲突的策

到这里. 我们还没有讲到 Transform 的应用. 属性匹配是 Transform 的基础. 当匹配不成功的时候, 会根据 Transform 定义的 FromTo 尝试组成转化规则. 下面是一个简单的转换

DependencyHandler dependencies = project.getDependencies();
        dependencies.registerTransform({
            it.getFrom().attribute(ArtifactAttributes.ARTIFACT_FORMAT, "ap");
            it.getTo().attribute(Attribute.of("artifactType", String.class), "ap-info");
            it.artifactTransform(ApInfoTransform.class);
        });
 apC.getIncoming().artifactView({
            config ->
                config.attributes {
                    container -> container.attribute(Attribute.of("artifactType", String.class), "ap-info")
                }
        }).getArtifacts().getArtifactFiles().getFiles();

定义了一个转换规则: 由 ap 转化成 ap-info .具体转换在 ApInfoTransform

数据的流向是: 根据 apC 的属性匹配到 lib 工程的 apLib 的属性. container 的属性没有找到能够匹配的属性. 但是匹配到了 ApInfoTransform 的To, ApInfoTransform 的 From 匹配到了 apVar 的属性. 至此组成了一个转化规则, 由 apVar 获取到的产物,需经过 ApInfoTransform 转化后返回.

0x03 Android Plugin 的应用

Android Plugin 正式使用 Gradle Transform 是在 3.0 上.

1. Android Plugin 2.3.0 实现

2.3.0

处理的是 AAR

  1. AAR 的来源有两个: 一种是 AAR 依赖 通过网络下载或本地获取, 一种是 Android Library Module 通过 bundleXX 任务打包出来的 AAR.
  2. Application Plugin 通过依赖关系收集所有的AAR, 再通过 PrepareLibraryTask 将所有 AAR 文件的解压, 用 AndroidDependency 来管理这些 AAR . 通过自己管理这些目录. 组织出不同的资源目录, 如 jar ,Jni,Aidl,Proguard ...

2. Android Plugin 3.0.0 实现

3.0

处理的是 attribute 产物
这里的 attribute 产物来源主要是两种,
一种是 AAR 依赖, 通过网络下载AAR, 通过 ExtractAarTransform 将 ARTIFACT_FORMAT 为 aar 转化成 android-exploded-aar 的产物. 再通过不不同的 AarTransform 将 ARTIFACT_FORMAT 为 android-exploded-aar 转化成不同的属性的产物.
一种是通过使用 Android Library Module . 通过往 Configuration 输出不同属性的产物.

3. 对比:

对比 2.3.0 来说 通过将 Transform 的功能将 AAR 文件的处理的时机和缓存全部移交给了 Gradle,
好处

  1. Transform 的处理是只有一次, 可以节省多余重复的解压.
  2. 缓存是全局的. 所有的工程都能复用同一个缓存.

同时由于 Gradle 并没有对 AAR 依赖做特别的适配, 导致在2.3.0 上处理依赖的时候,需要对依赖进行二次管理, Android Plugin 3.0.0 不需要这些额外的处理,只需要通过定义不同的 Transform 获取到对应的产物就好了.

0x03 Android Plugin Transform 和 Gradle Transform

  • 维度不同
    Android Plugin Transform 有两个维度一个是 ContentType ,一个是 Scope, 一个产物只有一个ContentType 和 Scope , Gradle 维度是 Attribute ,一个产物有多个 Attribute.
  • 扩展能力不同
    Android Plugin Transform 不支持自定义 ContentType , Gradle 支持自定义 Attribute .
  • 缓存不同
    Android Plugin Transform 将结果记录是在一个大的文件目录下, 通过不同的文件名称来表示不同 Scope 下的 ContentType.
    Gradle Transform 是将 Transform 输出的结果记录在 Gradle 的文件缓存中,
    由于记录的方式不同, 导致的结果是 Android plugin Transform 的一个输入对应一个输出,即使你并不对输入做任何操作, 也需要将输入的文件 copy 到输出的文件夹下.才能被下一个 Transform 正确识别. 而 Gradle Transform 不同, 记录是结果, 你完全可以不对输出目录做任何的事情, 直接将原有的输出地址返回回去.
  • 缓存范围不同
    Android Plugin Transform 缓存是 Application 级别的. 在 build/intermediates/transforms/目录下以transforms名称命名的.
    Gradle Transform 是全局的, 在.gradle/caches/transforms-1/files-1.1/下, 文件名称是由所有关键信息进行 hash 算出来的.
  • 应用不同
    Gradle Transform 应用的是依赖的产物。 将产物A 转化成 产物B, 不支持同属性的转化.
    Android Plugin Transform 应用的是打包过程中的产物如 so, classs, res,dex。 这些信息大部分是由依赖产物加工而来的。支持相同ContentType 的转化.

0x04 总结

通过上述的对比, 我们可以知道 两个 Transform 应用在不同场景, Gradle Transform 是对依赖进行处理. 这种处理是全局性的.而 Android Plugin Transform 是对打包过程中中间产物的再处理, 是针对 Application 的.

0x05 尾巴

这一块的代码看蛮久的, 因为涉及的代码比较多, 面比较广, 需要有一个比较好的耐心和比较久的时间. 通过这一块的梳理, 对 Android plugin 3.0 和 Gradle 4.1 的依赖的管理有更好的理解.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容