通过Gradle动态修改Manifest文件

1. Gradle

Gradle是开源的自动化构建工具,基于JVM,有良好的扩展性和性能,对IDE支持,支持编写自定义任务,在Android领域,用于构建和编译应用的资源和源代码,然后将它们打包成可供您测试、部署、签署和分发的 APK,另外在插件化和热修复的领取中起重要的作用。

gradle构建有三个阶段

  • 初始化
    分析哪些module将参与构建,settings.gradle文件会被解析。
  • 配置
    处理module的build脚本,处理属性和task的依赖关系,build.gradle文件会被解析配置
  • 执行
    按照配置过程处理的任务依赖关系相继执行task

2. 通过Gradle动态修改Manifest文件

例如:在release包manifest中动态添加标签为meta-data的渠道号channel<meta-data android:name=“channel" android:value=“1001" />

思路

  1. 在项目配置结束的task中,找到生成Release的manifest的task
  2. 通过该task找到输出的manifest文件并读取文本内容
  3. 使用Groovy的Xml Api对manifest进行修改
  4. 修改后重新写,输出至原始目录

完整代码如下,代码解释见3.X

project.afterEvaluate {  //3.1
    android.applicationVariants.all { ApplicationVariant variant ->  //3.2
        String variantName = variant.name.capitalize()  //3.3
        def processManifestTask = project.tasks.getByName("process${variantName}Manifest")  //3.4

        processManifestTask.doLast { pmt ->  //3.5
            String manifestPath = "$pmt.manifestOutputDirectory/AndroidManifest.xml"  //3.6
            def manifest = file(manifestPath).getText()  //3.7

            if (project.hasProperty("channel")) {
                def channelNo = project.property("channel")
                
                def xml = new XmlParser().parseText(manifest)  //3.8 
                xml.application[0].appendNode("meta-data", ['android:name': 'channel', 'android:value': channelNo])
                def serialize = XmlUtil.serialize(xml)
                file(manifestPath).write(serialize)
            }
        }
    }
}

终端中运行: gradle clean aR -P channel=101
查看apk中的manifest文件发现渠道号已经动态的添加进去了


image.png

3.1 project

project和build.gralde一一对应,在构建初始化期间,Gradle Project为每个参与构建的项目组装一个对象。

project.afterEvaluate(Closure closure)
参数是一个闭包,在项目配置后立即调用。project作为参数传递给闭包。

3.2 ApplicationVariant

Android的构建变体,可以理解为一个版本,例如debug,release等等,在项目配置结束后可以通过AppExtension.getApplicationVariants().all方法获取,或者通过DefaultGroovyMethods.each()方法获取。


官方ApplicationVariant文档说明.png

由api可知,使用all可以处理后面添加进来的构建变体,使用each只能处理当前的,所以建议用all。

3.3 variant.name.capitalize()

通过查看源码发现capitalize()是org.codehaus.groovy.runtime下StringGroovyMethods的一个方法,查看Groovy Api:


image.png

将第一个字母变大写,便于后续字符串拼接获取manifest,
variant.name= release
variant.name.capitalize()= Release

3.4 project.tasks.getByName(String name)

    T getByName(String name) throws UnknownTaskException;

根据Api可知,通过task的名称获取该task

3.5 task.doLast

task是gradle的执行单元,gradle的构建是通过taks的执行来完成。
定义名为hello的task如下:

task hello {
    printf "hello\n"
}

hello.doLast {
    printf "doLast\n"
}

执行gradle clean

> Configure project :app
hello

执行gradle hello

> Configure project :app
hello
> Task :app:hello
doLast

可以看到在执行一些配置任务时候hello任务也会一起执行,但是hello的doLast并没有执行,如果不需要在配置时候执行我们的自定义的任务,可以使用doLast来完成。

3.6 manifestOutputDirectory

通过该task的manifest输出路径获取manifest文件

 processManifestTask.doLast { pmt ->
            String manifestPath = "$pmt.manifestOutputDirectory/AndroidManifest.xml"
.......

代码是个闭包,pmt是processManifestTask的自定义简称,可以不定义,那么代码为

 processManifestTask.doLast { ->
            String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"
.......

3.7 file(manifestPath).getText()

 public static String getText(File file) throws IOException {
        return IOGroovyMethods.getText(newReader(file));
    }

传入个file对象通过BufferedReader读取从而获取文本

3.8 操作XML

使用Groovy解析XML的最常用方法如下:

  • groovy.util.XmlParser
  • groovy.util.XmlSlurper
相同点
  • 两者都是基于SAX它们都是低内存占用
  • 两者都可以更新/转换XML
不同点
  1. 返回值
  • XmlSlurper. parseText返回GPathResult
  • XmlParser. parseText返回Node
  1. 使用场景
  • 如果将现有文档转换成另一个文档,那么推荐用XmlSlurper
  • 如果只有少许节点,也推荐用XmlSlurper,这样不用在内存中创建完整的结构
  • 如果想同时更新和阅读,那么推荐用XmlParser

3.8.1 添加节点

用到的api:appendNode(XmlParser为例)

 def xml = new XmlParser().parseText(manifest)
 xml.application[0].appendNode("meta-data", ['android:name': 'channel', 'android:value': channelNo])

此时xml.application[0]是获取第一个application节点,虽然manifest只有一个application,也需要带上[0],例如获取application内的第一个activity节点为:

 xml.application.activity[0]

3.8.2 序列化并更新

将修改过的xml进行序列化为原生manifest,再通过文件路径写入更新

def serialize = XmlUtil.serialize(xml)
file(manifestPath).write(serialize)

引用参考
groovy Xml Api解析
groovy Api
迁移到 Android Plugin for Gradle 3.0.0
Android DSL官方文档
全面理解Gradle - 定义Task

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