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

Gradle构建过程包括三个阶段:初始化、配置和执行。初始化阶段分析哪些module将参与构建,解析settings.gradle文件。配置阶段处理module的build脚本,处理属性和task的依赖关系,解析并配置build.gradle文件。执行阶段按照配置过程处理的任务依赖关系相继执行task。

通过Gradle动态修改Manifest文件的思路如下:

1. 在项目配置结束的task中,找到生成Release版本的manifest的task。

2. 通过该task找到输出的manifest文件并读取文本内容。

3. 使用Groovy的XmlApi对manifest进行修改。

4. 修改后重新写入,输出至原始目录。

以下是完整的代码示例,代码解释见3.X部分:

```groovy

// 获取Manifest文件路径

def manifestPath = project.buildDir.parent.file('app/src/main/AndroidManifest.xml')

// 读取Manifest文件内容

def manifestContent = new File(manifestPath).text

// 使用Groovy的XmlApi对Manifest文件进行修改

def modifiedManifestContent = manifestContent.replaceAll('

// 将修改后的Manifest内容写回文件

new File(manifestPath).write(modifiedManifestContent)

```

3. X:在上述代码中,首先通过`project.buildDir.parent.file('app/src/main/AndroidManifest.xml')`获取Manifest文件的路径。然后,使用`new File(manifestPath).text`读取Manifest文件的内容。接下来,使用Groovy的XmlApi对Manifest文件进行修改,这里我们将`

以下是对给定内容的重构:

```groovy

project.afterEvaluate {

android.applicationVariants.all { variant ->

String variantName = variant.name.capitalize()

def processManifestTask = project.tasks.getByName("process${variantName}Manifest")

processManifestTask.doLast { pmt ->

String manifestPath = "$pmt.manifestOutputDirectory/AndroidManifest.xml"

def manifest = file(manifestPath).getText()

if (project.hasProperty("channel")) {

def channelNo = project.property("channel")

def xml = new XmlParser().parseText(manifest)

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

接下来,我们来分析一下Gradle的相关知识。

3.1 project和build.gradle一一对应,在构建初始化期间,Gradle Project为每个参与构建的项目组装一个对象。project.afterEvaluate(Closure closure)参数是一个闭包,在项目配置后立即调用。project作为参数传递给闭包。

3.2 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

variant.name= releasevariant.name.capitalize()= Release

3.4 project.tasks.getByName(String name)T getByName(String name) throws UnknownTaskException;根据API可知,通过task的名称获取该task。

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

"} hello.doLast { printf "doLast

"}执行gradle clean > Configure project :apphello

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

3.6 manifestOutputDirectory

通过该task的manifest输出路径获取manifest文件。例如:

```groovy

processManifestTask.doLast { pmt ->

String manifestPath = "$pmt.manifestOutputDirectory/AndroidManifest.xml"

}

```

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

```groovy

processManifestTask.doLast { -->

String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"

}

```

3.7 file(manifestPath).getText()

要读取文件内容,可以使用以下方法:

```groovy

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。

不同点:返回值。XmlSlurper.parseText返回GPathResult,XmlParser.parseText返回Node。使用场景:如果将现有文档转换成另一个文档,那么推荐用XmlSlurper;如果只有少许节点,也推荐用XmlSlurper,这样不用在内存中创建完整的结构。

如果想要同时更新和阅读XML文件,推荐使用XmlParser。下面是具体的操作步骤:

3.8.1 添加节点

首先,我们需要使用XmlParser的`parseText`方法解析XML文件。例如:

```scala

val xml = new XmlParser().parseText(manifest)

```

接下来,我们可以使用`appendNode`方法向特定节点添加新的子节点。以application节点为例:

```scala

xml.application[0].appendNode("meta-data", ["android:name" -> "channel", "android:value" -> channelNo])

```

在这个例子中,我们获取了第一个application节点(虽然manifest文件中只有一个application节点),然后向其内部的第一个activity节点添加了一个名为"meta-data"的子节点。

3.8.2 序列化并更新

完成对XML文件的修改后,我们需要将修改后的XML进行序列化为原生manifest格式,然后通过文件路径将其写入更新后的XML文件。具体操作如下:

```scala

val serializedXml = XmlUtil.serialize(xml)

file(manifestPath).write(serializedXml)

```

这样,我们就完成了XML文件的更新和读取操作。