Android Gradle插件开发

1. Gradle插件是什么

Gradle插件(Plugin)是一种用于扩展和定制Gradle构建系统功能的机制。Gradle插件可以执行各种任务,包括编译代码、执行测试、打包文件、生成文档等等。插件可以访问和操作Gradle的构建模型,如项目、任务、依赖关系等等,从而实现对构建过程的控制和定制。
许多流行的工具和框架,例如Kotlin,Spring Boot,Android都有相应的Gradle插件。
比如熟悉的Android插件com.android.application

plugins {
    id 'com.android.application'
}

2. 为什么需要插件

  • 定制:如果需要在编译期做一些插桩,Hook之类的自定义操作,也需要用到编译插件
  • 封装:把具体的逻辑抽出去,只需运行插件,不需要写在某个build.gradle文件中
  • 复用:把通用的逻辑抽离出去,需要用的时候apply应用插件即可,不需要一遍遍的复制,也可以提供给别的项目使用

3. 编写插件位置

  • 写在build.gradle文件中,作用域当前project
  • 写在buildSrc里,作用域为当前项目所有
  • 写在单独项目里,发布后可提供给所有项目

4. 编写插件

编写一个插件Plugin,只需要实现Plugin接口,并实现唯一apply方法即可。
在build.gradle文件中

class YiRanPlugin implements Plugin<Project>{

    @Override
    void apply(Project target) {
        println("this is plugin ${this.class.name}")
    }
}

apply plugin: YiRanPlugin

apply方法是调用的PluginAware接口的apply()方法,参数是一个map,用来映射Plugin id

点击sync,输出结果
在这里插入图片描述
Task是Project里的方法,我们可以通过Project取创建一个Task,apply方法中提供了Project对象,可以通过Plugin去创建一个Task

class YiRanPlugin implements Plugin<Project>{

    @Override
    void apply(Project target) {
        println("this is plugin ${this.class.name}")
        target.task("YiRanPluginTask"){
            task ->
                task.doLast{
                    println("this is Plugin ${this.class.name},create a task ${task.name}")
                }
        }
    }
}

apply plugin: YiRanPlugin

我们在Plugin里创建了一个Task,这时候sync是不会执行Task里面打印的东西,因为是doLast在 task 执⾏过程中被执⾏,发⽣在 execution 阶段。因此需要单独执行

./gradlew YiRanPluginTask    

输出

> Configure project :app
this is plugin YiRanPlugin

> Task :app:YiRanPluginTask
this is Plugin YiRanPlugin,create a task YiRanPluginTask

当我们依赖YiRanPlugin插件的时候,这个apply就会把插件放到PluginContainer里,同时这个apply也是在编译阶段执行Plugin接口的apply()方法,所有sync同步后构建会有输出。

5. 自定义插件扩展

自定义插件的时候经常会有这种自定义配置的需求,通过自定义的配置可以让我们的插件提供更丰富的能力。这些配置就是通过扩展插件来的。

5.1 订阅扩展对象

可以是一个接口,也可以是一个类

interface YiRanPluginExtension{
    Property<String> getTitle()
}

5.2 把扩展添加给Plugin并使用

class YiRanPlugin implements Plugin<Project>{

    @Override
    void apply(Project target) {
        println("this is plugin ${this.class.name}")
        def extension = target.extensions.create("YiRan",YiRanPluginExtension)
        target.task("YiRanPluginTask"){
            task ->
                task.doLast{
                    println("this is Plugin ${this.class.name},create a task ${task.name}")
                    println("-------")
                    println(extension.title.get())
                }
        }
    }
}

target.extensions.create方法接收两个参数:

  • 1.名字,比如android,YiRan
  • 2.扩展对象,然后返回这个扩展对象,通过这个扩展对象的方法可以获取自定义的配置参数

5.3 配置参数

有两种方式可以写

YiRan.title = "YiRan"
YiRan{
    title = "YiRan"
}

如果没有设置配置参数的话,Gradle也提供了默认值的配置

extension.title.convention("YiRan")

5.4 嵌套扩展

向我们app目录下的build.gradle文件中,android{}里面还有defaultConfig{}

android {
    namespace 'com.example.gradlestudy'
    compileSdk 35

    defaultConfig {
        applicationId "com.example.gradlestudy"
        minSdk 24
        targetSdk 34
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

嵌套扩展其实就是套娃

5.4.1 定义扩展
class YiRanPluginExtension{
    String title
    int chapterNum
    SubExtension subExtension

    YiRanPluginExtension(Project project){
        subExtension = project.extensions.create("sub",SubExtension.class)
    }
}

class SubExtension{
    String author
}

增加定义了一个SubExtension类,然后在YiRanPluginExtension实例化的时候加到ExtensionContainer中。
类嵌套的话,不能写成内部类,不然编译识别不过。

5.4.2 获取扩展属性
class YiRanPlugin implements Plugin<Project>{

    @Override
    void apply(Project target) {
        println("this is Plugin:${this.class.name}")
        def extension = target.extensions.create("YiRan",YiRanPluginExtension)
        target.task("YiRanTaskPluginTask"){ task ->
            task.doLast{
                println("this is Plugin ${this.class.name},create a task ${task.name}")
                println("title = ${extension.title}")
                println("chapter = ${extension.chapterNum}")
                println("author = ${extension.subExtension.author}")
            }
        }
    }
}

可以看到和上面的例子,少了Property对象的.get()方法,如果需要的话,在类对象定义即可,例如
增加setter/getter方法

class YiRanPluginExtension{
    String title
    int chapterNum
    SubExtension subExtension

    YiRanPluginExtension(Project project){
        subExtension = project.extensions.create("sub",SubExtension.class)
    }

    void setTitle(String name){
        title = name
    }

    String getTitle(){
        return title
    }
}

使用的时候

extension.setTitle("调用get方法")
extension.getTitle()
5.4.3 使用
YiRan{
    title = "测试一下"
    chapterNum = 100
    sub{
        author = "YiRan"
    }
}

闭包配置中多了一个sub{}的闭包,就是在YiRanPluginExtension类中定义的

5.4.4 执行
./gradlew YiRanTaskPluginTask       
5.4.5 输出

在这里插入图片描述

6. 编写在单独项目里

上面的例子,我们的Plugin是写在build.gradle文件中,而在实际项目中,为了复用,一般都是写在buildSrc或者单独项目中。
测试写一个打印项目中所有依赖的插件

6.1 新建Module

类型选择Library,或下面的Java or Kotlin Library
在这里插入图片描述

6.2 新建文件添加依赖

6.2.1 添加依赖

在plugin>build.gradle文件中依赖插件:

//java、gradleApi()依赖
apply plugin:'java-gradle-plugin'
//kotlin
apply plugin: 'org.jetbrains.kotlin.jvm'
6.2.2 新建类

新建一个DependenciesPlugin类
在这里插入图片描述

6.3 本地发布

6.3.1 Maven插件

在plugin>build.gradle文件中先依赖一个maven发布的插件’maven-publish’

apply plugin: 'maven-publish'

dependencies {

    implementation 'com.android.tools.build:gradle:8.6.1'

}

6.3.2 发布配置

添加发布配置


group 'com.example.plugin'
version '1.0.1'

publishing {
    //配置Plugin GAV
    publications {
        maven(MavenPublication){
            groupId = group
            artifactId = 'dependencies'
            version = version
        }
    }

    //配置仓库地址
    repositories {
        maven {
            url layout.buildDirectory.dir("maven-repo")
        }
    }
}
6.3.3 执行发布操作
./gradlew publish

build文件夹下生成有本地发布配置的maven-repo文件夹
在这里插入图片描述

6.4 使用

1.settings.gradle文件中配置仓库地址

pluginManagement {
    repositories {
    // ...
        maven {
            url './plugin/build/maven-repo'
        }
}
}
dependencyResolutionManagement {
        repositories {
        // ...
        maven {
            url './plugin/build/maven-repo'
        }
    }
}

libs.versions.toml中添加插件依赖

[plugins]
//-----
dependencies = { id = "com.example.plugin.dependencies", version.ref = "dependencies" }

app目录build.gradle引入

plugins {
//------
    alias(libs.plugins.dependencies)
}

本地依赖使用的时候,要先发布,再依赖插件,否则就会出现cannot found找不到依赖的情况。
编译查看效果

> Configure project :app
---> com.example.plugin.DependenciesPlugin

能够正确打印

6.5 具体功能实现

上面只是一个打印,继续实现我们的功能,把所有的依赖打印出来。
打印依赖的方式有很多,比如可以使用Gradle命令

./gradlew app:dependencies

改造Plugin

package com.example.plugin

import com.android.build.gradle.AppExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleVersionIdentifier

class DependenciesPlugin:Plugin<Project> {

    companion object{
        const val TAG = "DependenciesPlugin  >>>>>"
    }

    override fun apply(target: Project) {
        println("---> ${this.javaClass.name}")

        //获取扩展对象
        val extension:DependenciesPluginExtension = target.extensions.create("printDependencies",DependenciesPluginExtension::class.java)

        //配置阶段完成才能读取参数,才能拿到所有依赖
        target.afterEvaluate {

            extension.getEnable.convention(false)

            if(extension.getEnable.get()){

                println("$TAG 已开启依赖打印")

                val androidExtension:AppExtension = target.extensions.getByType(AppExtension::class.java)

                androidExtension.applicationVariants.all { applicationVariant->
                    println("$TAG >>>>>> applicationVariant.getName() = ${applicationVariant.name}")

                    val configuration:Configuration = applicationVariant.compileConfiguration
                    val allDependencies:Set<Dependency> = configuration.allDependencies

                    val androidLibs = ArrayList<String>()
                    val otherLibs = ArrayList<String>()

                    configuration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.forEach {
                            resolvedDependency ->
                        val identifier: ModuleVersionIdentifier = resolvedDependency.module.id
                        if(identifier.group.contains("androidx")||identifier.group.contains("com.google")||identifier.group.contains("org.jetbrains")){
                            androidLibs.add(identifier.group+":"+identifier.name+":"+identifier.version)
                        }else{
                            otherLibs.add(identifier.group+":"+identifier.name+":"+identifier.version)
                        }
                    }
                    println("--------------官方库 start--------------");
                    androidLibs.forEach(System.out::println);
                    println("--------------官方库 end--------------");

                    println("--------------三方库 start--------------");
                    otherLibs.forEach(System.out::println);
                    println("--------------三方库 end--------------");
                }

            }else{
                println("$TAG 已关闭依赖打印")
            }
        }

    }
}

project.afterEvaluate方法中去获取扩展配置,因为apply plugin的执行时机早于扩展配置,否则获取不到扩展配置的值
扩展:

package com.example.plugin

import org.gradle.api.provider.Property


interface DependenciesPluginExtension {

    val getEnable:Property<Boolean>

}

使用:

printDependencies {
    setGetEnable(true)
}

编译输出:
在这里插入图片描述
通过独立项目中定义插件,把所有依赖的区分打印出来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值