为了账号安全,请及时绑定邮箱和手机立即绑定

通过Gradle动态修改Manifest文件

标签:
Android

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文件发现渠道号已经动态的添加进去了


https://img1.sycdn.imooc.com//5d2c8a2a0001f89807240370.jpg

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()方法获取。


https://img1.sycdn.imooc.com//5d2c8a3b0001567c07150279.jpg

官方ApplicationVariant文档说明.png


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

3.3 variant.name.capitalize()

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


https://img1.sycdn.imooc.com//5d2c8a5100015e6807110252.jpg

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 :apphello

执行gradle hello

> Configure project :apphello
> Task :app:hellodoLast

可以看到在执行一些配置任务时候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)



作者:小波_po
链接:https://www.jianshu.com/p/f5cd48019109


点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消