自定义Gradle二进制插件

二进制插件

我们出于自己的目的,要开发实际业务场景的插件来辅助项目构建。 自定义二进制插件,可以使用 java,kotlin,或者 groovy 来编码,因为他们都是基于JVM的编程语言,最终都会生成class交给JVM去执行。groovy提供了更多插件相关的api,有一些情况用groovy能直接达成效果,而不需要去引入其他类库。

以kotlin为例:

创建一个java library

每个androidStudio版本的操作界面可能各不相同,这是 Android Studio Electric Eel | 2022.1.1 Patch 2 版本的截图。

20230706102529imagepng

引入 必要的 依赖

包括两部分,一个是gradleApi,必须引入。

另外就是其他的,我们编写编码需要用到的其他依赖库,比如 发送网络请求的 okhttp,生成二维码的zxing ,接入华为云的 obs等等。

plugins {
    id 'java-library'
    id 'org.jetbrains.kotlin.jvm'  // kotlin依赖
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation gradleApi() // 必须的gradle依赖

    // 引入其他工具类库
    implementation 'com.squareup.okhttp3:okhttp:3.14.6' // 使用最新版okhttp居然无法引用,很诡异
    implementation 'com.huaweicloud:esdk-obs-java-bundle:3.21.8' // 引入 华为云
    implementation 'com.google.zxing:core:3.4.1' // 二维码生成器
    implementation 'org.springframework:spring-core:2.5.6'
    implementation 'net.dongliu:apk-parser:2.5.3' // apk解析工具

}

新建一个Project实现类

import com.android.build.gradle.AppExtension
import com.tw.lib.bean.AutoUploadBean
import org.gradle.api.Plugin
import org.gradle.api.Project

class AutoUploadPlugin : Plugin<Project> {

    override fun apply(project: Project) {

        println("======== 自动上传插件初始化 =========")
        // 配置外来参数, 表示插件的使用者可以在gradle文件中写 相关的参数
        val autoUploadBean = project.extensions.create("autoUpload", AutoUploadBean::class.java)

        project.afterEvaluate {
            val appExtension = it.extensions.getByType(AppExtension::class.java)
            appExtension.applicationVariants.all { v ->
                v.outputs.all { out ->
                    val file = out.outputFile // 输出的apk文件
                    val name = out.name

                    project.tasks.create(
                        "auto_upload_$name",
                        AutoUploadTask::class.java,
                        file,
                        autoUploadBean
                    )
                }
            }

        }


    }
}

比如上面我要做一个实现自动上传的插件 AutoUploadPlugin ,它必须实现

Plugin<Project>接口,并实现其中的apply方法。方法内部写上你要做的操作。

一个project是由多个Task组合而成,下面的代码中,我我在apply方法中创建了一个名叫 autoUpload的 额外参数,并创建了一个名为 AutoUploadTask的 任务。

创建 AutoUploadTask 任务

public class AutoUploadTask extends DefaultTask {

    private File file;
    private final AutoUploadBean autoUploadBean;

    @Inject
    public AutoUploadTask(File file, AutoUploadBean autoUploadBean) {
        setGroup("autoUpload");
        this.file = file;
        this.autoUploadBean = autoUploadBean;
    }

    @TaskAction
    public void action() throws IOException {
        System.out.println("做你想做的事情");
    }
}

这里有一些要素必须提到,

首先,setGroup是自定任务的分组,这个分组最终会呈现在androidStudio右侧的gradle面板中。

其次,步骤3中执行了这么一段代码:

                    project.tasks.create(
                        "auto_upload_$name",
                        AutoUploadTask::class.java,
                        file,
                        autoUploadBean
                    )

这其实是在反射创建 AutoUploadTask对象,后面的file和autoUploadBean其实就是入参,所以我们必须提供一个带这两个参数的构造函数。

最后,在接收到 file和 autoUploadBean 外来参数之后,我们可以在 重写的action方法中使用它们来完成我们自己想要的的操作。

AutoUploadBean类的内容为:

open class AutoUploadBean {

    // 华为OBS账号
    var userName: String? = null
    // 华为OBS密码
    var pwd: String? = null
    // 华为OBS桶
    var domain: String? = null

    // 是否使用分段上传
    var usePartUpload: Boolean = false

    // 下载二维码的输出目录
    var buildRootDir: String? = null


}

到这里,插件基本上就编写完成了。

插件注册

每一个插件都有全局唯一一个 id ,这个id注册在 /src/main/resources/META-INF/gradle-plugins中,我们在 这个目录下,创建一个 自定义名称的com.tw.autoUpload.properties文件,自定义的名称就是我们插件的唯一ID,文件内容为:implementation-class=com.tw.lib.AutoUploadPlugin ,也就是我们自定义Plugin的全类名。

使用插件

在 要使用插件模块的build.gradle中

apply plugin:'com.tw.autoUpload'

或者

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android' 

    id 'com.tw.autoUpload'  // 使用插件
}

并且指定插件的额外参数:

autoUpload {
    userName 'c00293388'
    pwd 'Wd2A@zFJ1234'
    domain 'hwstaff_DigitalPayment'

    usePartUpload false

    buildRootDir 'C:\Users\zwx1245985\Desktop\saved'
}

执行插件任务

至此完成,经过编译之后,我们就能在 任务分组 autoupload之下,使用 auto_upload任务去执行上传操作了。

20230706104027imagepng

插件的发布

发布二进制插件有两种方式,

发布到naven仓库

此处以 发布到localMaven为例说明:

编写发布脚本

在我们创建的java library的 build.gradle中,写入内容:

publishing {
    publications {
        AutoUploadPlugin(MavenPublication) {
            from components.java
            groupId 'com.tw'
            artifactId 'aotuUpload'
            version '1.0'
        }

    }
}

上面有4处需要我们做修改:

  1. AutoUploadPlugin 这是自定义插件的名称,直接用 我们创建的继承 org.gradle.api.Plugin<Project>的类名
  2. groupId 插件发布的群组
  3. artifactId 群组下的插件名称
  4. version 版本

后面3项直接决定了插件的唯一性。

建立mavenLocal环境

在 本地建立localMaven环境(如何创建环境,看这里!www.jianshu.com/p/cb941c74a… ),然后执行 本工程的 publishToMavenLocal 命令,将它发布到本地maven仓库下。

引用插件

发布之后就是引用插件:

buildscript {
    repositories {
        mavenLocal()
    }

    dependencies {
        classpath 'com.tw:aotuUpload:1.0'
   }
}

随后的 apply plugin:'com.tw.autoUpload'之后的步骤就参照前文使用插件章节。

直接引用jar包

我们创建了一个 autoUploadPlugin 的java library ,在编译之后,module下会存在一个 jar包 autoUploadPlugin.jar, 它的效果和本地maven相同,只不过 引用的方式必须是在 build.gradle中:

buildscript{
    dependencies {
        classpath files('lib/autoUploadPlugin.jar')
    }
}

同样,apply plugin:'com.tw.autoUpload' 之后的步骤也参照前文使用插件章节。

Gradle插件

纯gradle插件则简单很多,以下是一个简单案例:

定义插件

在app 目录下 创建一个 config.gradle 文件,内容为:

ext {
    dependenciesx = [            appcompat          : [name: 'androidx.appcompat:appcompat', version: '1.6.1'],
            material           : [name: 'com.google.android.material:material', version: '1.8.1'],
            constraintlayout   : [name: 'androidx.constraintlayout:constraintlayout', version: '2.1.4'],
            navigation_fragment: [name: 'androidx.navigation:navigation-fragment', version: '2.5.3'],
            navigation_ui      : [name: 'androidx.navigation:navigation-ui', version: '2.5.3'],
            junit              : [name: 'junit:junit', version: '4.13.2'],
            junitx             : [name: 'androidx.test.ext:junit', version: '1.1.5'],
            espresso_core      : [name: 'androidx.test.espresso:espresso-core', version: '3.5.1'],

    ]
}

其中放置了很多第三方依赖的 name 和 version

引入插件

apply from: "config.gradle"

如果想把 config.gradle发布到远程(内网或外网)的话,

则可以使用 apply from :"http://xxx.com/config.gradle" 的方式引入。

使用插件

dependencies {

    dependenciesx.each { dependencyName, dependency ->
        implementation "${dependency.name}:${dependency.version}"
    }
}

很简洁的三步走。

两种方式的对比

它们具有不同的优缺点,具体情况下可以根据项目的特点和需求来决定应该使用哪种方式。

  1. 二进制插件(Binary Plugin):

    • 优点:

      • 性能更好:二进制插件是使用 JVM 字节码编写的,因此它们通常比纯 Gradle 插件的脚本执行速度更快,尤其是对于大型项目来说。
      • 可以使用其他语言:二进制插件可以使用许多编程语言编写(如 Kotlin、Groovy、Java 等),因此你可以选择熟悉的语言来编写插件。
    • 缺点:

      • 开发复杂度高:二进制插件的开发需要实现插件的接口和声明插件的元数据。在一些简单的场景下,这可能会增加不必要的开发复杂性。
      • 部署和依赖管理稍复杂:使用二进制插件时,你需要构建插件并将其发布到仓库,然后才能在项目中引用并配置依赖关系。
  2. 纯 Gradle 插件(Script Plugin):

    • 优点:

      • 开发简单:纯 Gradle 插件使用 Groovy 或 Kotlin 等编写,因此开发过程更直接简单,不需要编译和发布。
      • 灵活性高:纯 Gradle 插件可以直接访问 Gradle 的内部对象和 API,能够更灵活地在构建过程中进行定制和扩展。
    • 缺点:

      • 性能相对较低:由于纯 Gradle 插件是以脚本形式执行的,相对于二进制插件会有一些性能的损耗。
      • 只能使用 Groovy/Kotlin:纯 Gradle 插件只能使用 Groovy 或 Kotlin 等特定的脚本语言,有一定的学习曲线。

在选择哪种方式时,你可以考虑以下因素:

  • 项目的规模和复杂度:对于较大、复杂的项目,二进制插件可能更适合,可以提供更好的性能和扩展性。
  • 开发资源和技能:如果你已经熟悉了 Groovy 或 Kotlin,那么纯 Gradle 插件可能更容易上手和开发。而如果你有其他语言的开发经验,二进制插件则提供了更多选择。
  • 部署和依赖管理的要求:如果你希望将插件发布到仓库并作为独立的模块使用,那么二进制插件是更合适的选择。而纯 Gradle 插件则更适合内部使用或作为项目本身的一部分。

综上所述,二进制插件和纯 Gradle 插件各有优劣,选择哪种方式要根据具体项目的要求和约束来进行决策。

全部评论

相关推荐

03-03 19:02
已编辑
东华理工大学 Node.js
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务