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文件的更新和读取操作。