第8篇——插件开发指南(二十五)
发表时间:2023-09-20 17:00:29
文章来源:炫佑科技
浏览次数:155
菏泽炫佑科技
第8篇——插件开发指南(二十五)
本文经作者许可发表。
前言
插件开发占据了一定比例的高级知识,尤其是性能优化领域,基本都覆盖了,也和我们日常的编译打包密切相关。 另外,很多招聘要求明确要求有插件开发经验。 所以即使大多数人在日常开发中可能不会使用插件开发,但仍然向往。 本文为您带来该系列的第8篇文章——插件开发指南,希望能给您带来收益。
什么是插件
() 是一种用于扩展和自定义构建系统功能的机制。 它是一个强大的构建自动化工具,用于构建和管理各种类型的项目,从简单的 Java 应用程序到复杂的多模块企业级项目。 插件提供了灵活性,允许开发人员根据特定需求添加自定义行为和功能。
插件可以执行各种任务,包括编译代码、执行测试、打包文件、生成文档等等。 插件可以访问和操作构建模型,例如项目、任务、依赖项等,以实现构建过程的控制和定制。
提供了丰富的插件生态系统,可以使用现有的官方插件或第三方插件来增强构建过程。 很多流行的框架和工具,比如,Boot等,都有相应的插件,使得与这些技术栈的集成变得更加简单和高效。
比如我们熟悉的插件com..:
plugins {
id 'com.android.application'
}
通过编写自己的插件,您可以自定义和扩展构建系统以满足特定项目的需求。 您可以在插件中定义自定义任务、配置扩展、操作项目属性、应用其他插件等。 插件使构建过程可控、可定制,从而提高开发效率。
为什么要写插件?
编写插件的意义:
封装,提取具体逻辑,项目只需要运行插件即可,不需要放在某个build中。 文件,这会降低构建的可读性。
复用,提取通用逻辑软件开发,使用时只需应用插件即可。 不需要一遍又一遍的复制第8篇——插件开发指南(二十五),也可以提供给其他项目使用;
定制:如果在编译时需要做一些插桩、Hook等定制操作,还需要使用编译插件。
插件写在哪里?
上面我们介绍了Task,也提到Task写在哪里,但是写在哪里呢?
插件可以写在3个地方:
和Task一样,它是写在build中的。 文件且范围是当前的;
写进去,范围是当前项目所拥有的;
写在一个单独的项目中,发布后可以供所有项目使用。
根据自己的需要,结合插件作用域,可以写在不同的位置。
自定义插件
编写插件其实很简单。 你只需要实现接口并实现唯一的apply方法。
我们直接在build中写就可以了。 文件:
class YechaoaPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("这是插件:${this.class.name}")
}
}
apply plugin: YechaoaPlugin
//apply(plugin: YechaoaPlugin)
这实际上是一个内联类。
编写后不要忘记应用依赖项。 第9行的apply方法是被调用接口的apply()方法,参数是一个map,用于映射ID。
同步输出:
> Configure project :app
这是插件:YechaoaPlugin
...
上一篇文章对Task的详细讲解中提到,Task是 中的一个方法,所以我们需要通过它来创建一个Task。 示例中的类实现了接口并实现了唯一的apply方法,而apply方法提供了一个对象,所以我们也可以在其中创建一个Task。
class YechaoaPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("这是插件:${this.class.name}")
project.task("YechaoaPluginTask") { task ->
task.doLast {
println("这是插件:${this.class.name},它创建了一个Task:${task.name}")
}
}
}
}
如上,我们在 . 此时sync不会执行Task中的打印。 我们必须单独执行任务。
实施:
./gradlew YechaoaPluginTask
输出:
> Task :app:YechaoaPluginTask
这是插件:YechaoaPlugin,它创建了一个Task:YechaoaPluginTask
OK,*基本的写法就是这么简单。
结合上面两个输出,无论是简单的打印进去,还是创建一个Task进去,当我们依赖一个插件的时候,也就是apply:,这个apply就会把插件放进去,同样的,同时,这个apply也是在编译阶段执行接口的apply()方法,所以sync执行构建后会有输出,执行的Task也在有向无环图中。
自定义插件扩展
在本系列的第二章中,我们分析了 { } 闭包是如何从源代码中得出的。 { } 闭包是我们非常熟悉的配置。 通过DSL,我们经常在里面进行配置等。
在自定义插件的时候,经常会需要这种自定义配置。 通过这些自定义配置,我们的插件可以提供更丰富的功能。 这些配置是通过扩展插件提供的。
6.1. 定义扩展对象
interface YechaoaPluginExtension{
Property<String> getTitle()
}
它可以是一个接口或一个类。
6.2. 添加扩展并使用它们
class YechaoaPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("这是插件:${this.class.name}")
def extension = project.extensions.create("yechaoa", YechaoaPluginExtension)
project.task("YechaoaPluginTask") { task ->
task.doLast {
println("这是插件${this.class.name},它创建了一个Task:${task.name}")
println(extension.title.get())
}
}
}
}
..() 方法接收两个参数:
**个是名称,例如;
第二种是扩展对象,然后返回扩展对象。 通过该扩展对象的方法可以获取定制的配置参数。
6.3. 配置参数
yechaoa.massage = "【Gradle-8】Gradle插件开发指南"
一个配置可以直接省略,也可以这样写:
yechaoa {
massage = "【Gradle-8】Gradle插件开发指南"
}
如果没有设置配置参数,也提供默认值设置:
extension.title.convention("默认配置title")
如果是类对象,则定义/。
如果有多个配置怎么写? 只需扩展多个配置属性即可。
6.4. 嵌套扩展
如下,{ } 还包含 { }
android {
namespace 'com.yechaoa.gradlex'
compileSdk 32
defaultConfig {
applicationId "com.yechaoa.gradlex"
...
}
}
嵌套扩展其实很简单,就像套娃一样。
上面我们使用接口来定义扩展属性。 我们改变一下写法,用类对象来定义。
6.4.1. 定义扩展
class YechaoaPluginExtension {
String title
int chapter
SubExtension subExtension
YechaoaPluginExtension(Project project) {
subExtension = project.extensions.create('sub', SubExtension.class)
}
}
class SubExtension {
String author
}
再定义一个类,并在实例化时将其添加到 on 中。
如果你想嵌套类,那没问题。 它们必须是内联类,否则编译器将无法识别它们。
6.4.2. 获取扩展属性
class YechaoaPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("这是插件:${this.class.name}")
def extension = project.extensions.create("yechaoa", YechaoaPluginExtension)
// 设置默认值 可以定义set()方法 然后在这里set
project.task("YechaoaPluginTask") { task ->
task.doLast {
println("这是插件${this.class.name},它创建了一个Task:${task.name}")
println("title = ${extension.title}")
println("chapter = ${extension.chapter}")
println("author = ${extension.subExtension.author}")
}
}
}
}
与上面接口定义的例子相比,缺少了对象的.get(),并且也去掉了默认值的设置。 如果你愿意,只需在类对象中定义/方法即可,其他逻辑保持不变。
6.4.3. 使用
yechaoa {
title = "【Gradle-8】Gradle插件开发指南"
chapter = 8
sub {
author = "yechaoa"
}
}
在闭包配置中,有一个额外的 sub{ } 闭包,它是在我们的 on 类中定义的。
6.4.4. 执行
./gradlew YechaoaPluginTask
6.4.5。 输出
> Task :app:YechaoaPluginTask
title = 【Gradle-8】Gradle插件开发指南
chapter = 8
author = yechaoa
6.4.6。 完整代码
class YechaoaPluginExtension {
String title
int chapter
SubExtension subExtension
YechaoaPluginExtension(Project project) {
subExtension = project.extensions.create('sub', SubExtension.class)
}
}
class SubExtension {
String author
}
class YechaoaPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("这是插件:${this.class.name}")
def extension = project.extensions.create("yechaoa", YechaoaPluginExtension)
// 设置默认值 可以定义set()方法 然后在这里set
project.task("YechaoaPluginTask") { task ->
task.doLast {
println("这是插件${this.class.name},它创建了一个Task:${task.name}")
println("title = ${extension.title}")
println("chapter = ${extension.chapter}")
println("author = ${extension.subExtension.author}")
}
}
}
}
apply plugin: YechaoaPlugin
yechaoa {
title = "【Gradle-8】Gradle插件开发指南"
chapter = 8
sub {
author = "yechaoa"
}
}
现在 { } 这个配置是不是很熟悉:
yechaoa {
title = "【Gradle-8】Gradle插件开发指南"
chapter = 8
sub {
author = "yechaoa"
}
}
和{}一样吗:
android {
namespace 'com.yechaoa.gradlex'
compileSdk 32
defaultConfig {
applicationId "com.yechaoa.gradlex"
...
}
}
写在一个单独的项目中
我们上面的内容都写在 build 中了。 文件,但一般在实际项目中,为了更好的复用,通常会写在单独的项目中或者单独的项目中。
构建中的写法还是有一些区别的。 文件并写入一个单独的项目中。 我们来看看在单独的项目中怎么写(等于)。
让我们简单一点,只需编写一个打印项目中所有依赖项的程序即可。
7.1. 新的
新建一个名称,类型选择Java或以下。
创建后会有默认的文件目录,多余的文件可以删除。
我们可以看到主文件夹下有一个java文件夹。 你可以用java写,也可以使用和学习。 无论你喜欢什么,你都可以在主文件下创建一个与语言相对应的新文件夹界面,例如文件夹。
7.2. 创建一个新文件并添加依赖项。 7.2.1. 创建一个新类。
创建一个新类:
但是这个时候你还是写不出来,因为你没有依赖相关的API。
7.2.2. 添加依赖项
在6.4及以后版本中,不需要添加()来配置依赖。 直接用java--就可以完成,它会自动将java和()依赖添加到项目中。
并且您不需要在 src/main//META-INF/-/xxx 中配置您的 -class。 像以前一样。 只需配置 { } 即可自动生成 META-INF 描述文件。
依赖于 >build.xml 中的插件。 文件:
plugins {
id 'java-gradle-plugin'
}
配置如下:
gradlePlugin{
plugins{
DependenciesPlugin{
id = 'com.yechaoa.plugin.dependencies'
implementationClass = 'com.yechaoa.plugin.DependenciesPlugin'
}
}
}
6.4之前:
implementation-class=com.yechaoa.plugin.DependenciesPlugin
因为以前这些文件夹和配置都是手动的,非常繁琐。 相比之下,现在就过得愉快多了。
7.3. 写作
package com.yechaoa.plugin;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
/**
* GitHub : https://github.com/yechaoa
* CSDN : http://blog.csdn.net/yechaoa
*
* Created by yechao on 2023/8/8.
* Describe :
*/
class DependenciesPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println(">>>>>>>> " + this.getClass().getName());
}
}
创建一个新类来实现该接口;
在 apply 方法中实现您自己的逻辑,如本示例所示。
至此,基本原型已经就位。
使用以下命令添加依赖项:
apply plugin: 'com.yechaoa.plugin.dependencies'
但现在它不能在外部项目中使用。 如果直接引用this的话,是找不到的(未找到)。
Plugin with id 'com.yechaoa.plugin.dependencies' not found.
因为这是在一个单独的项目中写的,准确的说是和其他项目没有任何关系。 如果你想找到这个插件,你就必须发布它。
7.4. 本地发布
本地发布比远程发布简单得多。 远程发布虽然不难,只是比较麻烦。
7.4.1. Maven 插件
首先,比较常用的仓库是maven。 在>构建中。 文件,首先依赖maven发布的插件'maven-'。
plugins {
id 'maven-publish'
}
dependencies {
implementation 'com.android.tools.build:gradle:7.3.0'
}
7.4.2. 发布配置
添加发布配置:
group 'com.yechaoa.plugin'
version '1.0.0'
publishing {
// 配置Plugin GAV
publications {
maven(MavenPublication) {
groupId = group
artifactId = 'dependencies'
version = version
from components.java
}
}
// 配置仓库地址
repositories {
maven {
url layout.buildDirectory.dir("maven-repo")
}
}
}
7.4.3. 执行发布操作
./gradlew publish
或者点击右侧可视化面板中的Run:
7.4.4. 生成产品
好了,现在build文件夹下多了一个带有本地发布配置的maven-repo文件夹。
可以再次确认maven元数据和pom文件:
<metadata>
<groupId>com.yechaoa.plugingroupId>
<artifactId>dependenciesartifactId>
<versioning>
<latest>1.0.0latest>
<release>1.0.0release>
<versions>
<version>1.0.0version>
versions>
<lastUpdated>20230809154815lastUpdated>
versioning>
metadata>
7.5。 使用
好了,本地发布完成。 如果要使用这个插件的话,流程和我们正常依赖插件是一样的。
三个步骤:
在.xml文件中配置插件仓库地址文件。
pluginManagement {
repositories {
// ...
maven {
url './maven-repo'
}
}
}