编译期间修改manifest文件

175 阅读2分钟

概述

之前写过一篇文章说明如何编译期间修改manifest,但是那个方法读取的是本地manifest清单,会同时修改本地的代码,下面给出优化后的实现方案,不会有这个问题。

实现

创建Plugin作为入口

推荐以插件的形式进行修改, 维护方便一些。

class PluginGroovy implements Plugin<Project> {

    private String variantName

    @Override
    void apply(Project target) {
        
    }
}

获取当前的编译变体名称

/**
 * 获取当前编译的变体名称
 *
 * @param project
 */
private void getVariantNameInBuild(Project project) {
    try { // 根据实际的情况处理为真正的变体名称
        variantName = project.gradle.startParameter.taskNames[0]
    } catch (Exception e) {
        e.printStackTrace()
    }
}

检验当前的变体是否符合要求

private boolean isValidVariantName() {
    variantName != null && variantName.length() > 0 && !variantName.contains("clean")
}

添加修改manifest的Task

/**
 * 添加处理manifest的Task
 *
 * @param project
 */
private void addTaskForPackageManifest(Project project) {
    // 找到 processManifestTask
    def processManifestTask = project.getTasks().getByName(String.format("process%sManifest", variantName.capitalize()))

    // 创建自定义任务
    def modifyManifestTask = project.tasks.create("modify${variantName.capitalize()}Manifest") {
        doLast {
            // 获取编译后的 AndroidManifest.xml 文件路径
            def manifestFile = new File("${project.buildDir}/intermediates/merged_manifests/${variantName}/AndroidManifest.xml")

            if (manifestFile.exists()) {
           // 执行修改
                handlerVariantManifestFile(manifestFile)
            } else {
                println("Manifest file not found: ${manifestFile}")
            }
        }
    }
    // 确保自定义任务在 processManifestTask 任务之后执行
    processManifestTask.finalizedBy(modifyManifestTask)
}

修改文件并重新写入

/**
 * 处理单个变体的Manifest文件
 */
private static void handlerVariantManifestFile(File manifestFile) {
    SAXReader saxReader = new SAXReader()
    def mergedManifestDoc = saxReader.read(manifestFile)
    modifyBaseManifestContent(mergedManifestDoc, manifestFile)
}

/**
 * 修改Manifest文件
 *
 * @param document
 * @param xmlFile
 */
private static void modifyBaseManifestContent(Document document, File xmlFile) {
    Element rootNode = document.getRootElement()

    Element applicationElement = rootNode.element("application")

    if (applicationElement != null) {

        Attribute directBootNode = applicationElement.attribute("directBootAware")
        if (directBootNode != null) {
            directBootNode.setValue("false")
        }

        OutputFormat format = OutputFormat.createPrettyPrint()
        format.setEncoding("UTF-8")
        XMLWriter writer = new XMLWriter(
                new OutputStreamWriter(new FileOutputStream(xmlFile)), format)
        writer.write(document)
        writer.close()
    }
}

完整实现

private void modifyManifest(Project target) {
    println("modifyManifest start! need directBoot : $needDirectBoot")
    // 在sync中无法获取到variantName
    getVariantNameInBuild(target)
    System.out.println(String.format("Welcome %s ManifestProject", variantName))
    if (!isValidVariantName()) {
        return
    }
    // 添加处理manifest文件的task
    addTaskForVariantAfterEvaluate(target)
}

/**
 * 获取当前编译的变体名称
 *
 * @param project
 */
private void getVariantNameInBuild(Project project) {
    try { // 因为ape插件的原因, 需要做还原
        variantName = project.gradle.startParameter.taskNames[0].replace("apeSign", "")
    } catch (Exception e) {
        e.printStackTrace()
    }
}

private boolean isValidVariantName() {
    variantName != null && variantName.length() > 0 && !variantName.contains("clean")
}

/**
 * 在项目配置完成之后添加task
 * @param project
 */
private void addTaskForVariantAfterEvaluate(Project project) {
    //在项目配置完成后,添加自定义Task
    project.afterEvaluate {
        //为当前变体的task都加入到这个任务队列中。
        //所以通过project.getTasks().each {}去匹配每个task的startsWith&&endsWith的逻辑是一致的
        //并且这种性能会更高
        //直接通过task的名字找到ProcessApplicationManifest这个task
        addTaskForPackageManifest(project)
    }
}

/**
 * 添加处理manifest的Task
 *
 * @param project
 */
private void addTaskForPackageManifest(Project project) {
    // 找到 processManifestTask
    def processManifestTask = project.getTasks().getByName(String.format("process%sManifest", variantName.capitalize()))

    // 创建自定义任务
    def modifyManifestTask = project.tasks.create("modify${variantName.capitalize()}Manifest") {
        doLast {
            // 获取编译后的 AndroidManifest.xml 文件路径
            def manifestFile = new File("${project.buildDir}/intermediates/merged_manifests/${variantName}/AndroidManifest.xml")

            if (manifestFile.exists()) {
                handlerVariantManifestFile(manifestFile)
            } else {
                println("Manifest file not found: ${manifestFile}")
            }
        }
    }
    // 确保自定义任务在 processManifestTask 任务之后执行
    processManifestTask.finalizedBy(modifyManifestTask)
}

/**
 * 处理单个变体的Manifest文件
 */
private static void handlerVariantManifestFile(File manifestFile) {
    SAXReader saxReader = new SAXReader()
    def mergedManifestDoc = saxReader.read(manifestFile)
    modifyBaseManifestContent(mergedManifestDoc, manifestFile)
}

/**
 * 修改Manifest文件
 *
 * @param document
 * @param xmlFile
 */
private static void modifyBaseManifestContent(Document document, File xmlFile) {
    Element rootNode = document.getRootElement()

    Element applicationElement = rootNode.element("application")

    if (applicationElement != null) {

        Attribute directBootNode = applicationElement.attribute("directBootAware")
        if (directBootNode != null) {
            directBootNode.setValue("false")
        }

        OutputFormat format = OutputFormat.createPrettyPrint()
        format.setEncoding("UTF-8")
        XMLWriter writer = new XMLWriter(
                new OutputStreamWriter(new FileOutputStream(xmlFile)), format)
        writer.write(document)
        writer.close()
    }
}

修改效果

解压apk可以看到,directBootAware属性被成功修改为false

1728439691920.png