美文网首页
Gradle 插件练习-动态移除权限

Gradle 插件练习-动态移除权限

作者: 潇风寒月 | 来源:发表于2020-08-07 19:00 被阅读0次

    1. 开始之前

    Gradle DSL 文档

    Gradle基于Groovy,而Groovy基于Java,最后始终得运行在JVM之上.Gradle、build.gradle、settings.gradle之类的最终都会被搞成一个对象,然后才能执行.

    • Gradle 对象: 每次执行gradle taskName时,Gradle都会默认构造出一个Gradle对象.在执行过程中,只有这么一个Gradle对象,一般很少去定制它.
    • Project对象: 一个build.gradle就对应着一个Project对象.
    • Settings对象: 一个settings.gradle就对应着一个Settings对象.

    它们的生命周期节点如下:

    image

    2. 创建Plugin

    先创建buildSrc这个module,用于开发插件.(不清楚的可以看我之前发的Gradle系列(四) Gradle插件).
    然后新建一个插件类: ManifestDemoPlugin.

    import org.gradle.api.Plugin
    import org.gradle.api.Project
    class ManifestDemoPlugin implements Plugin<Project> {
        @Override
        void apply(Project project) {
            
        }
    }
    

    上面这份代码是标准代码,写插件都得继承自Plugin.

    3. 分析

    需求: 假设是移除android.permission.READ_PHONE_STATE权限(有时三方库aar里面可能会定义一些权限,但是又不能让app有这些权限,就需要移除掉.这里仅仅是为了练习Gradle,其实有更好的方式移除权限tools:remove).

    思路: 我们需要拿到合并之后的AndroidManifest.xml文件,且在打包之前修改这个AndroidManifest.xml文件,将android.permission.READ_PHONE_STATE内容移除.

    但是我们怎么hook这个合并AndroidManifest.xml文件的时机,从而拿到清单文件内容呢?首先通过./gradlew tasks --all命令看看有哪些task,因为合并清单文件肯定是一个task里面做的,我们只需要在这个task之后执行我们写的代码逻辑即可.

    //task实在太多了,这里只是节选.
    > Task :tasks
    
    ......
    app:makeApkFromBundleForDebug
    app:makeApkFromBundleForRelease
    app:mergeDebugAndroidTestAssets
    app:mergeDebugAndroidTestGeneratedProguardFiles
    app:mergeDebugAndroidTestJavaResource
    app:mergeDebugAndroidTestJniLibFolders
    app:mergeDebugAndroidTestNativeLibs
    app:mergeDebugAndroidTestResources
    app:mergeDebugAndroidTestShaders
    app:mergeDebugAssets
    app:mergeDebugGeneratedProguardFiles
    app:mergeDebugJavaResource
    app:mergeDebugJniLibFolders
    app:mergeDebugNativeLibs
    app:mergeDebugResources
    app:mergeDebugShaders
    app:mergeDexRelease
    app:mergeExtDexDebug
    app:mergeExtDexDebugAndroidTest
    app:mergeExtDexRelease
    app:mergeLibDexDebug
    app:mergeLibDexDebugAndroidTest
    app:mergeProjectDexDebug
    app:mergeProjectDexDebugAndroidTest
    app:packageDebug
    app:packageDebugAndroidTest
    app:packageDebugBundle
    app:packageDebugUniversalApk
    app:packageRelease
    app:packageReleaseBundle
    app:packageReleaseUniversalApk
    app:parseDebugIntegrityConfig
    app:parseReleaseIntegrityConfig
    app:preBuild
    app:preDebugAndroidTestBuild
    app:preDebugBuild
    app:preDebugUnitTestBuild
    prepareKotlinBuildScriptModel
    app:prepareKotlinBuildScriptModel
    app:prepareLintJar
    app:prepareLintJarForPublish
    ......
    
    

    其中有一个task叫app:mergeDebugResources,翻译过来就是合并资源嘛,看起来就是我们要找的.下面是Android Plugin Task的大致含义.

    Android Plugin Task 含义1 Android Plugin Task 含义2

    4. 开始写代码

    class ManifestDemoPlugin implements Plugin<Project> {
        @Override
        void apply(Project project) {
            //在afterEvaluate,配置完成之后才能拿到那些task完整的有向图
            project.afterEvaluate {
                //1. 找到mergeFreeDebugResources这个task
                def mergeDebugResourcesTask = project.tasks.findByName("mergeFreeDebugResources")
                if (mergeDebugResourcesTask != null) {
                    //2. 创建一个task
                    def parseDebugTask = project.tasks.create("ParseDebugTask", ParseDebugTask.class)
                    //3. 添加一个mergeDebugResourcesTask结束后立马执行的task: parseDebugTask
                    mergeDebugResourcesTask.finalizedBy(parseDebugTask)
                }
            }
        }
    }
    
    1. 我们需要在Project配置完成之后才能拿到所有的task,因为这个时候才真正生成了完整的有向图task依赖.
    2. 其次是通过API: project.tasks拿到所有的task(API文档地址在这里),然后再findByName方法找到这个task.
    3. 这时我们得创建一个自己的task并在mergeFreeDebugResources执行完成之后立马开始执行.

    来看看我们的Task该怎么写:

    class ParseDebugTask extends DefaultTask {
    
        @TaskAction
        void doAction() {
            //1. 找到清单文件这个file
            def file = new File(project.buildDir, "/intermediates/merged_manifests/freeDebug/AndroidManifest.xml")
            if (!file.exists()) {
                println("文件不存在")
                return
            }
    
            //2. 获得文件内容
            def fileContent = file.getText()
    
            removePermission(file, fileContent)
        }
    
        /**
         * 动态给清单文件移除权限
         * @param rootNode Node
         * @param file 清单文件
         */
        void removePermission(File file,String fileContent) {
            //方案1  这样会把所有权限都移除了,暂时没找到合适的办法
            //def rootNode = new XmlParser().parseText(fileContent)
            //def node = new Node(rootNode, "uses-permission"/*,["android:name" : "android.permission.READ_PHONE_STATE"]*/)
            //rootNode.remove(node)
            //def updateXmlContent = XmlUtil.serialize(rootNode)
            //println(updateXmlContent)
    
            //方案2 读取到xml内容之后,将制定权限的字符串给替换掉,,妙啊 妙啊
            fileContent = fileContent.replace("android.permission.READ_PHONE_STATE", "")
            println(fileContent)
            //将字符串写入文件
            file.write(fileContent)
        }
    
    }
    
    1. 首先Task得继承自DefaultTask
    2. 合并之后的清单文件是在build/intermediates/merged_manifests/freeDebug/目录下,先得到这个文件
    3. 通过file.getText()获取文件内容,再将内容里面的字符串"android.permission.READ_PHONE_STATE"移除掉.
    4. 然后再将字符串写入清单文件(待会儿打包的时候就是用的这个文件进行打包的).

    顺便,咱还可以再来一个,动态添加一个权限:android.permission.INTERNET

    /**
    * 动态给清单文件添加权限
    * @param rootNode Node
    * @param file 清单文件
    */
    void addPermission(File file,,String fileContent) {
        def rootNode = new XmlParser().parseText(fileContent)
        //3. 添加网络权限  这里得加上xmlns:android
        //<uses-permission android:name="android.permission.INTERNET"/>
        //xmlns:android="http://schemas.android.com/apk/res/android"
        rootNode.appendNode("uses-permission", ["xmlns:android": "http://schemas.android.com/apk/res/android",
                                                "android:name" : "android.permission.INTERNET"])
    
        //还可以动态将meta-data加到Application中                                        
        //rootNode.application[0].appendNode("meta-data", ['android:name': 'appId', 'android:value': 546525])  
    
        //4. 拿到修改后的xml内容
        def updateXmlContent = XmlUtil.serialize(rootNode)
        println(updateXmlContent)
    
        //5. 将修改后的xml 写入file中
        file.write(updateXmlContent)
    }
    

    5. 总结

    刚开始的时候API非常不熟悉,咱得疯狂地查API.尽量想一些需求做练习,多写写代码,多查查API,熟悉这个过程.Gradle插件非常重要.

    就这?

    相关文章

      网友评论

          本文标题:Gradle 插件练习-动态移除权限

          本文链接:https://www.haomeiwen.com/subject/cbrqdktx.html