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

1. 开始以前

Gradle DSL 文档html

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

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

它们的生命周期节点以下:node

2. 建立Plugin

先建立buildSrc这个module,用于开发插件.(不清楚的能够看我以前发的Gradle系列(四) Gradle插件). 而后新建一个插件类: ManifestDemoPlugin.android

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

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

3. 分析

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

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

可是咱们怎么hook这个合并AndroidManifest.xml文件的时机,从而拿到清单文件内容呢?首先经过./gradlew tasks --all命令看看有哪些task,由于合并清单文件确定是一个task里面作的,咱们只须要在这个task以后执行咱们写的代码逻辑便可.markdown

//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的大体含义.网络

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该怎么写:app

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插件很是重要.

就这?

相关文章
相关标签/搜索