定制Gradle任务自动打包jar

日常Android开发中,很大部分不需要自定义Gradle插件,但是需要定制一些task来满足需求,比如自定义打jar包,本文就借自定义打包来讲讲gradle的task使用方法和如何利用现在的task修改依赖关系。

自定义打包在N年前我也曾写过一篇博文,[使用gradle打包指定包名和类的jar](http://www.alloyteam.com/2015/03/shi-yong-gradle-da-bao-zhi-ding-bao-ming-he-lei-di-jar/),这是刚开始学习gradle时的做法,实现方法也就是在原有的gradle文件上建立一个Jar类型的task然后依赖编译过程的某个task然后调用相关apip实现自定义的功能

task makeJar(type:org.gradle.api.tasks.bundling.Jar) {
    //指定生成的jar名
    baseName 'sdk'
    //从哪里打包class文件
    from('build/intermediates/classes/debug/org/cmdmac/cloud/pluginsdk/')
    //打包到jar后的目录结构 
    into('org/cmdmac/cloud/pluginsdk/')
    //去掉不需要打包的目录和文件 
    exclude('test/', 'BuildConfig.class', 'R.class')
    //去掉R$开头的文件 
    exclude{ it.name.startsWith('R$');} 
} 
 
makeJar.dependsOn(clearJar, build)
这种方式可以达到目的,但是有个问题是,这个task需要在命令行./gradlew makeJar执行或者在Androd Studio的gradle task界面上手动触发,一般我们运行生成apk并运行都是直接点run的,这个过程是不会触发我们定义的task的,原因是原有的task依赖链中没有makeJar,那如何做到我们点run就能执行的自定义的task呢?

由前面的原因分析可以知道是因为我们的task没有在原来的依赖链中,解决方法就是把它加到原有的依赖链中!方法如下:

//配置检查完后
afterEvaluate {
    logger.log(LogLevel.ERROR, assemble.getTaskDependencies().toString())
    logger.log(LogLevel.ERROR, assemble.getTaskDependencies().getDependencies().toString())
    //让打包任务先依赖assemble任务
    def t = tasks.jarAopLib
    t.dependsOn assemble.getTaskDependencies().getDependencies()
    //让assemble任务再依赖jarAopLib,这样相当于在原有任务中插入任务jarAopLib
    assemble.dependsOn t
}

//打包任务
task jarAopLib(type: Jar) {
    archiveName = 'aop.jar'
    from('build/intermediates/classes/release')
    destinationDir = file('build/libs')
//    into('build/libs')
    exclude('org/cmdmac/aop/BuildConfig.class')
    exclude('org/cmdmac/aop/BuildConfig\$*.class')
    exclude('**/R.class')
    exclude('**/R\$*.class')
    include('org/cmdmac/aop/')
}

重写afterEvalute闭包(这是gradle配置完成后会调用的方法,gradle分配置task和运行task两个状态),修改原来的依赖关系,把自定义的task加入到依赖链中,此时点run会自动执行jarAopLib生成一个aop.jar文件在工程的build/libs下。


方法其实很简单,关键的是要了解熟悉gradle的机制还有gradle众多task类型以前相关的api,这个就得到gradle.com官网去学习才行了,比如就得知道要在afterEvaluate这里修改依赖关系,否则无从下手,另外得知道getTaskDependencies().getDependencies()函数的用法,这两个方法定义的其实有点奇怪的,也是试了多次才成功。

上面我们是把打包任务定义在外层,这种方式不能根据debug和release分开打包,其实也可以把任务定义在闭包里面根据variant的根据来实现分类型打包,如:

afterEvaluate {
    //遍历编译类型,一般是debug和release两种,用android.libraryVariants是因为我写在Library工程
    android.libraryVariants.each { variant ->
        //tasks对象包含了project下的所有task。这里根据编译类型实现命名不同动态创建task
        def t = tasks.create(name: "JarLibTask-${variant.name}", type: Jar) {
            archiveName  "aop-${variant.name}.jar"
            from('build/intermediates/classes/release')
            destinationDir = file('build/libs')
            exclude('org/cmdmac/aop/BuildConfig.class')
            exclude('org/cmdmac/aop/BuildConfig\$*.class')
            exclude('**/R.class')
            exclude('**/R\$*.class')
            include('org/cmdmac/aop/')
        }
        t.doLast {
            //打包完成时会执行
            System.out.println('打包完啦');
        }
        //修改依赖关系
        t.dependsOn assemble.getTaskDependencies().getDependencies()
        assemble.dependsOn t
    }
}
点run就会出现aop-debug.jar和aop-release.jar,怎么样,是不是挺好用的?

上面的task是动态创建的,创建task方式有挺多种,我自己总结了这么几种使用方式:

//用task带括号创建要注意,第一个参数不能name:'xxx',否则参数无效,变成一个匿名task
//匿名task,archiveName属性不生效
 def t = task("ttt${variant.name}", type: Jar) {
            archiveName  "aop-${variant.name}.jar"
            logger.log(LogLevel.ERROR, 'ttt')
        }
//有效,第一个参数为task名称
 def t = task("ttt${variant.name}", type: Jar) {
            logger.log(LogLevel.ERROR, 'ttt')
        }
//也有效,第一个参数为task名称
 def t = task(name: "ttt${variant.name}", type: Jar) {
            logger.log(LogLevel.ERROR, 'ttt')
        }
//也可以用tasks内置对象来创建
def t = tasks.create(name: 'xxx', type: Jar) {
	…
}
//也可以这样
def t = task name(type: Jar) {
	…
}

上面的例子中有看到可以在字符串中动态填入参数的用法,有点像java的String.format功能,这里需要注意的是groovy中要使用这个功能字符串需要使用双引号,单引号只是单纯的字符串,如:

//要用双引号才能字符串内带计算,单引号是纯字符串,下面这句输出结果为:'${variant.name      
  logger.log(LogLevel.ERROR, '${variant.name}')

总结 

gradle的task很强大,要学会gradle实现自定义的功能需要多学习gradle插件中实现的api和task类型等等知识

你可能感兴趣的:(android)