Gradle学习系列(三):Gradle插件

概述

又开始了一个新的系列,这个系列学习Gradle,目标就是彻底理解Gradle,主要还是做下自己理解的笔记,防止忘记

Gradle系列(一):Groovy学习

Gradle学习系列(二):Gradle核心解密

Gradle学习系列(三):Gradle插件

简介

Gradle本身只是提供了基本的核心功能,其他的特性比如编译Java源码的能力,编译Android工程的能力等等就需要通过插件来实现了。要想应用插件,需要把插件应用到项目中,应用插件通过 Project.apply() 方法来完成。在Gradle中一般有两种类型的插件,分别叫做脚本插件和对象插件。

脚本插件是额外的构建脚本,它会进一步配置构建,可以把它理解为一个普通的build.gradle。

对象插件又叫做二进制插件,是实现了Plugin接口的类,下面分别介绍如何使用它们。

脚本插件

比如我们在项目的根目录创建一个utils.gradle

def getxmlpackage(boolean x){ def file=new File(project.getProjectDir().getPath()+”/src/main/AndroidManifest.xml”); def paser = new XmlParser().parse(file) return paser.@package}ext{ getpackage = this.&getxmlpackage}复制代码

这就是一个简单的脚本插件,然后在app moudle引用这个脚本插件

apply from: rootProject.getRootDir().getAbsolutePath() + “/utils.gradle”复制代码

然后就可以调用脚本插件中的方法了

对象插件

对象插件就是实现了org.gradle.api.plugins接口的插件,对象插件可以分为内部插件和第三方插件。

内部插件

Gradle包中有大量的插件,比如我们经常用的java插件和c的插件,我们可以直接引入

apply plugin:’java’apply plugin:’cpp’复制代码三方插件

第三方的对象插件通常是jar文件,要想让构建脚本知道第三方插件的存在,需要使用buildscrip来设置。在buildscrip中来定义插件所在的原始仓库和插件的依赖 ,再通过apply方法配置就可以了。Android Gradle插件也属于第三方插件,如果我们想引入Android Gradle插件,可以这么写:

buildscript { repositories { //配置仓库 google() jcenter() } dependencies { //配置插件依赖 classpath ‘com.android.tools.build:gradle:3.5.3’ }}//然后就可以在需要的地方引入android 插件了apply plugin: ‘com.android.application’复制代码自定义对象插件

自定义一个插件主要是实现 org.gradle.api.Plugin

Build script

这种方式直接在构建脚本中写插件代码,但是这种方式只能在本文件中使用,比如我在app的build.gradle中直接加入如下代码

class myPlugin implements Plugin{ @Override void apply(Project project) { println(“myPlugin 执行了”) project.task(“myTask”){ doLast { println(“myTask执行了”) } } }}复制代码

然后引入在本文件定义的插件

apply plugin: myPlugin复制代码

这个就是直接在build.gradle 中实现了一个插件,在插件中定义了一个MyTask任务,但是这个插件只能在本文件中使用,我们直接执行Mytask任务./gradlew MyTask

> Task :mylibrary2:myTaskmyTask执行了执行阶段,task ‘:mylibrary2:myTask’耗时:1ms复制代码

这种方式实现的插件我们一般不使用,因为这种方式局限性太强,只能本Project,而其他的Project不能使用

buildSrc项目

buildSrc是Gradle默认的插件目录,编译Gradle的时候会自动识别这个目录,将其中的代码编译为插件,

首先先建立一个名为buildSrc的java Module,然后只保留build.gradle和src/main目录,其他全部删掉,注意名字一定是buildSrc,不然会找不到插件

然后修改Gradle中的内容

apply plugin: ‘groovy’ //必须apply plugin: ‘maven’dependencies { implementation gradleApi() //必须 implementation localGroovy() //必须 //如果要使用android的API,需要引用这个,实现Transform的时候会用到 //implementation ‘com.android.tools.build:gradle:3.3.0’}repositories { google() jcenter() mavenCentral() //必须}//把项目入口设置为src/main/groovysourceSets { main { groovy { srcDir ‘src/main/groovy’ } }}复制代码

创建入口目录,在src/main下创建代码入口目录,如下:

然后实现插件代码Text.groovy,注意文件后缀为groovy,文件要引入package com.renxh

package com.renxhimport org.gradle.api.Pluginimport org.gradle.api.Projectclass Text implements Plugin{ @Override void apply(Project project) { println(“执行自定义插件”) project.task(“haha”){ doLast{ println(“执行自定义插件 haha task”) } } }}复制代码接下来在main目录下创建resources目录,在resources目录下创建META-INF目录,在META-INF目录下创建gradle-plugins目录,在gradle-plugins目录下创建properties文件properties文件可以自己命名,但是要以.properties结尾,比如com.renxh.plugin.properties最后需要在properties文件中指明我们实现插件的类implementation-class=com.renxh.Text

到目前为止我们的插件项目已经写完了,在module引入我们写的插件apply plugin:’com.renxh.plugin’,然后执行插件的Task,./gradlew haha

输出

> Task :mylibrary:haha执行自定义插件 haha task执行阶段,task ‘:mylibrary:haha’耗时:0ms复制代码

这种形式的写法,在我们整个工程的module都可以使用,但也只是限制在本工程,其他工程不能使用

自定义module,上传maven

第二种写插件的方式他只能在本工程中使用,而其他的项目工程不能使用,有时候我们需要一个插件在多个工程中使用,这时候我们就需要把插件上传maven中

首先先建立一个java module,名字可以任意起,只保留build.gradle和src/main,其他文件都删除修改Gradle中内容apply plugin: ‘groovy’ //必须apply plugin: ‘maven’ //要想发布到Maven,此插件必须使用dependencies { implementation gradleApi() //必须 implementation localGroovy() //必须}repositories { mavenCentral() //必须}def group=’com.renxh.cusplugin’ //组def version=’2.0.0′ //版本def artifactId=’myplugin’ //唯一标示//将插件打包上传到本地maven仓库uploadArchives { repositories { mavenDeployer { pom.groupId = group pom.artifactId = artifactId pom.version = version //指定本地maven的路径,在项目根目录下 repository(url: uri(‘../repos’)) } }}复制代码

相比buildSrc方案,这个增加了Maven支持和uploadArchives这样的Task,这个Task作用就是把本地插件打包上传到本地的maven仓库,这里../repos表示项目根目录项下的repos,俩个.回退俩次,回退到项目的根目录

在src/main生成groovy文件和生成properties文件的步骤和buildSrc方案一致的

编写插件

class CustomPlugin implements Plugin{ @Override void apply(Project project) { project.task(“cusplugin”){ doLast{ println(“cusplugin任务执行111”) } } }}复制代码

然后执行./gradlew CusPlugin:uploadArchivesuploadArchives任务,然后就可以把repos目录布置到项目根目录了,如图:

repos目录就是本地的Maven仓库,com/renxh/cusplugin就是脚本中指定的group,myplugin也是脚本中指定的模块名字,是一个唯一标识,1.0.0就是脚本中version

生成本地的maven仓库之后,就需要引用本地maven仓库中的插件了,首先需要在根目录的build.gralde中加入如下:buildscript { repositories { google() jcenter() //引入本地仓库 maven { url uri(‘./repos’) //指定本地maven的路径,在项目根目录下 } } dependencies { classpath ‘com.android.tools.build:gradle:3.5.3’ //引入本地仓库中的插件依赖 classpath ‘com.renxh.cusplugin:myplugin:1.0.0’ // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }}复制代码classpath指定的路径格式,如下:

这三个参数都是在生成仓库的时候在build.gradle脚本中配置的

classpath ‘[groupId]:[artifactId]:[version]’ 复制代码配置完之后就可以在module中使用了apply plugin: ‘com.android.application’apply from: rootProject.getRootDir().getAbsolutePath() + “/utils.gradle”apply plugin:’com.renxh.cusplugin’复制代码最后就可以执行插件中的任务 ./gradlew app:cusplugin> Task :app:cusplugincusplugin任务执行111执行阶段,task ‘:app:cusplugin’耗时:0ms复制代码插件的扩展 Extension

我们也可以在插件运行的时候,给插件配置参数,插件的Extension就是用来把参数回传到插件中的

下面我就就给上面的插件添加Extension

首先建一个实体类,来接收参数

package com.renxh.cuspluginclass MyExtension { String name; int age;}复制代码

然后去插件类中添加扩展

class CustomPlugin implements Plugin{ @Override void apply(Project project) { //添加扩展 project.extensions.add(‘myextension’,MyExtension) project.task(“cusplugin”){ doLast{ println(“cusplugin任务执行111”) MyExtension extension = project.myextension //3.输出插件扩展属性 println “>>>>>> name: ${extension.name} age:${extension.age}” } } }}复制代码通过project.extensions.add将自定以的实体类添加到扩展中,并起个名字然后通过起的名字拿到扩展的实体类最后拿到扩展中的属性

修改完之后重新上传maven

上传完之后,就可以在module中使用扩展实体类了

apply plugin: ‘com.android.application’apply from: rootProject.getRootDir().getAbsolutePath() + “/utils.gradle”apply plugin:’com.renxh.cusplugin’myextension{ name ‘renxh’ age 27}复制代码

最后执行插件中的task,./gradlew app:cusplugin

> Task :app:cusplugincusplugin任务执行111>>>>>> name: renxh age:27执行阶段,task ‘:app:cusplugin’耗时:2ms复制代码嵌套Extension

我们在android中经常看到这种嵌套的扩展,如下:

android { compileSdkVersion 29 buildToolsVersion “29.0.2” defaultConfig { applicationId “com.aliyun.idrs.app” minSdkVersion 24 targetSdkVersion 29 versionCode 1 versionName “1.0” testInstrumentationRunner “androidx.test.runner.AndroidJUnitRunner” }}复制代码

defaultConfig就是嵌套在android的里面的,那我们改如何实现这种形式呢?

首先创建一个内部类

class Inner { String b void b(String b){ this.b=b }}复制代码

然后在原来的扩展中加入如下代码

class MyExtension { String name; int age; Inner text = new Inner() //创建内部Extension,名称为方法名 text void text(Action action) { action.execute(text) } //创建内部Extension,名称为方法名 text void text(Closure c) { org.gradle.util.ConfigureUtil.configure(c, text) }}复制代码

这里面的关键代码就是

Inner text = new Inner() //创建内部Extension,名称为方法名 inner void text(Action action) { action.execute(text) } //创建内部Extension,名称为方法名 inner void text(Closure c) { org.gradle.util.ConfigureUtil.configure(c, text) }复制代码

这俩个方法是用来创建内部扩展,实际的使用中只需要其中一个方法就行,需要注意的是方法的名字要和成员变量的名字相同

更改plugin中的代码

class CustomPlugin implements Plugin{ @Override void apply(Project project) { project.extensions.add(‘myextension’,MyExtension) project.task(“cusplugin”){ doLast{ println(“cusplugin任务执行111”) MyExtension extension = project.myextension //3.输出插件扩展属性 println “>>>>>> name: ${extension.name} age:${extension.age}inner:${extension.text.b}” } } }}复制代码

然后就可以在build.gradle中使用了

apply plugin:’com.renxh.cusplugin’myextension{ name ‘renxh’ age 27 text{ b “hahah” }}复制代码

执行插件,输出

> Task :app:cusplugincusplugin任务执行111>>>>>> name: renxh age:27 inner:hahah复制代码android中的嵌套Extension

image.png

我们可以点进去看下他们的源码,最终会追溯到BaseExtension类

private final DefaultConfig defaultConfig; private final NamedDomainObjectContainer buildTypes; public void defaultConfig(Action action) { this.checkWritability(); action.execute(this.defaultConfig); } public void buildTypes(Action


比丘资源网 » Gradle学习系列(三):Gradle插件

发表回复

提供最优质的资源集合

立即查看 了解详情