Android插桩修改Manifest文件

205 阅读3分钟

编译阶段修改Manifest文件属性

概述

起因:在开发业务过程中,遇到了需要根据不同的平台,修改Manifest属性的问题。为了只维护一套代码,打算在编译阶段修改这个属性。

在网上找资料,感觉比较少,自己后面成功之后记下来,方便回溯和分享。

编写插件作为入口

创建插件

/**
 * 插件入口
 */
class ManifestProject implements Plugin<Project> {
    String variantName
    @Override
    void apply(Project project) {
       
    }
}

导入项目

在项目app的build.gradle导入插件

apply plugin: ManifestProject

获取变体名称

这一步是需要找到,弄清楚自己项目的编译任务的变体,方便后面通过这些变体来查找具体的任务。

查看当前项目的所有Task

 ./gradlew tasks --all

在这个输出的结果中查找:(正则表达式---process.*?Manifest)

1719543994025.png 这个对应编译任务的这个:

1719544093434.png

那么,通过上面的操作,可以了解到:

  • 要查找的任务简单描述为"process变体名称Manifest"(根据自己项目输出的内容灵活调整)
  • 以上面为例,"Base"就是我的变体名称。

获取变体名称

通过代码获取当前的变体名称

/**
 * 插件入口
 */
class ManifestProject implements Plugin<Project> {
    protected static final String TAG = "ManifestPlugin"
    String variantName
    @Override
    void apply(Project project) {
       //创建ManifestExtension
        createManifestExtension(project)
        //获取变体
        getVariantNameInBuild(project)
        System.out.println(String.format("Welcome %s ManifestProject", variantName)
        //如果不是一个有效的variant,则直接返回
        if (!isValidVariantName()) {
            return
        }
    }
    
    /**
     * 配置扩展属性
     * @param project
     */
    void createManifestExtension(Project project) {
        project.getExtensions().create(TAG, ManifestExtension)
    }
    
    /**
     * 获取变体名称
     
     * @param project
     */
    void getVariantNameInBuild(Project project) {
        // 根据自己项目调整, 可能你的项目有定制化
        // 以上面为例, 需要去掉多余的前缀后缀之类的, 转换为"Base"
        // 可以打印出来看下, 如果没有定制, 就不用处理了
        variantName = project.gradle.startParameter.taskNames[0]
    }
    
    boolean isValidVariantName() {
        variantName != null && variantName.length() > 0 && !variantName.contains("clean")
    }
}

通过上面的代码,就可以在variantName中拿到当前的变体名称了。

创建修改Manifest的Task

下面的例子,通过修改Manifest,修改属性

android:directBootAware="true"

的值,例子中将true改为false。 实现如下:

/**
 * 具体修改manifest的Task
 *
 */
class ChangeDirectBootForPackageManifestTask extends DefaultTask {
    protected static String TAG = "ChangeDirectForPackageManifestFromManifestProject"
    private FileCollection manifestCollection
    private File mainManifestFile

    /**
     * 设置所有的 需要合并的Manifest文件
     * @param collection
     */
    void setManifestsFileCollection(FileCollection collection) {
        manifestCollection = collection
    }

    /**
     *
     * @param file
     */
    void setMainManifestFile(File file) {
        mainManifestFile = file
    }

    @TaskAction
    void doTaskAction() {
        //处理所有包下的AndroidManifest文件
        manifestCollection.each {
            handlerVariantManifestFile(it)
        }
        // 自己app下的
        handlerVariantManifestFile(mainManifestFile)
    }

    /**
     * 处理单个变体的Manifest文件
     */
    void handlerVariantManifestFile(File manifestFile) {
        if (!manifestFile.exists()) {
            return
        }
        def node = readManifestFromPackageManifest(manifestFile)
        writeManifestForPackageManifest(manifestFile, node)
    }

    /**
     * 读manifest内容
     * @param manifestFile
     * @return
     */
    Node readManifestFromPackageManifest(File manifestFile) {
        try {
            // xml 解析器
            XmlParser xmlParser = new XmlParser()
            def node = xmlParser.parse(manifestFile)
            boolean needChange = false
            Object key1 = null
            // 找见application下的属性
            Node application = node.application[0]
            if (application != null) {
                // 找到directBootAware属性, 如果不知道自己的属性名称, 可以加日志打印, 再针对修改
                application.attributes().find {
                    if ("{http://schemas.android.com/apk/res/android}directBootAware".equals(it.key.toString())) {
                        key1 = it.key
                        needChange = true
                    }
                }
                application.attributes().each { key, value ->
                    System.out.println("my key : " + key + " value: " + value)
                }
                System.out.println("needChange:  " + needChange)
                if (needChange) { // 修改属性为false
                    application.attributes().put(key1, false)
                }
            }
            return node

        } catch (ParserConfigurationException e) {
            e.printStackTrace()
        } catch (SAXException e) {
            e.printStackTrace()
        } catch (IOException e) {
            e.printStackTrace()
        }
    }

/**
 * 第四步:保存到原AndroidManifest文件中
 * @param manifestFile
 * @param node
 */
    void writeManifestForPackageManifest(File manifestFile, Node node) {
        // 修改写回到maniFest
        String result = XmlUtil.serialize(node)
        manifestFile.write(result, "utf-8")
    }
}

将修改Manifest的任务添加到编译任务中

...
    @Override
    void apply(Project project) {
        ....
        // 添加修改Task
        addTaskForVariantAfterEvaluate(project)
    }


/**
     * 在项目配置完成之后添加task
     * @param project
     */
    void addTaskForVariantAfterEvaluate(Project project) {
        //初始化 AddExportForPackageManifestTask
        ChangeDirectBootForPackageManifestTask addExportTask = project.getTasks().create(ChangeDirectBootForPackageManifestTask.TAG,
                ChangeDirectBootForPackageManifestTask)
        //在项目配置完成后,添加自定义Task
        project.afterEvaluate {
            //为当前变体的task都加入到这个任务队列中。
            //所以通过project.getTasks().each {}去匹配每个task的startsWith&&endsWith的逻辑是一致的
            //并且这种性能会更高
            //直接通过task的名字找到ProcessApplicationManifest这个task
            addExportTaskForPackageManifest(project, addExportTask)
        }
    }
        void addExportTaskForPackageManifest(Project project, ChangeDirectBootForPackageManifestTask beforeAddTask) {
        // 利用之前获取到的变体名称, 这里查找Task
        ProcessApplicationManifest processManifestTask = project.getTasks().getByName(String.format("process%sManifest", variantName))
        beforeAddTask.setManifestsFileCollection(processManifestTask.getManifests())
        beforeAddTask.setMainManifestFile(processManifestTask.getMainManifest().get())
        processManifestTask.dependsOn(beforeAddTask)
    }
....

验证

a4a3a1d8ac13a6f588984ba13a9dc63.png

参考文章