美文网首页
自定义Gradle插件

自定义Gradle插件

作者: HoooChan | 来源:发表于2017-08-10 11:37 被阅读280次

    Gradle插件让我们把可以复用的构建逻辑打包起来,这样就可以用到其他项目当中。我们可以在Gradle中引用自己的自定义插件。下面介绍几种自定义插件的方法:

    • Build script
    • buildSrc project
    • Standalone project

    Build script

    这种方式直接把插件的代码写入build script当中,好处是除了编写代码之外不用配置其他的引入参数,坏处就是只能在此build script当中使用这个插件,无法复用。在创建自定义插件时需要编写一个插件的实现。 Gradle实例化插件并使用Plugin.apply()方法调用插件实例。当我们引用插件时:“apply plugin: 'id'”,apply()方法就会被执行。

    新建一个自定义插件

    apply plugin: 'com.android.application'
    apply plugin: MyCustomGradle
    
    class MyCustomGradle implements Plugin<Project>{
    
        @Override
        void apply(Project project) {
            project.task("customplugin"){
                doLast {
                    println "This is my custom plugin!"
                }
            }
        }
    }
    
    android {
         ......
    }
    

    运行这个插件定义的Task:

    D:\Android\Workspace\CustomGradlePlugin>gradlew "customplugin"
    NDK is missing a "platforms" directory.
    If you are using NDK, verify the ndk.dir is set to a valid NDK directory.  It is currently set to D:\Android\sdk\ndk-bundle.
    If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
    
    Incremental java compilation is an incubating feature.
    :app:customplugin
    This is my custom plugin!
    
    BUILD SUCCESSFUL
    
    Total time: 2.29 secs
    

    值得注意的是Gradle会为每个引用这个插件的project创建一个这个plugin 的实例。在apply中给Project添加了一个新属性,然后在script中配置这个属性的值。配置的时候这个配置块的名字要和新属性的名字对应。这里要注意apply的时候并没有执行到script,所以如果在apply中获取配置的值是获取不到的,需要在task里面添加doLast或doFirst,等到执行task的时候再去获取值。

    从脚本中获取输入

    大多数的插件都需要从build script当中获取配置信息。Project通过ExtensionContainer来追踪那些传给插件的配置信息。为了能够从脚本中获取信息,我们要在extension container中添加一个Java Bean:

    project.extensions.create("myplugin", MyPluginExtension)
    

    MyPluginExtension:

    class MyPluginExtension{
        String message;
    }
    

    这样我们就在Project中新建了一个名为“myPlugin”且类型为“MyPluginExtension”的属性,可以直接通过project.myPlugin访问。而且在分析到myPlugin{}这个script block时就会把{}这个闭包的代理设置为MyPluginExtension,在MyPluginExtension的上下文中运行这个闭包。

    myplugin{
        message "Thank you!";
    }
    

    就相当于:

    myPluginExtension.setMessage("Thank you!")
    

    因为Groovy会自动加上setter和getter,所以我们在定义MyPluginExtension时不必再写。之后再插件中获取配置的值的方式则为:

    project.myPlugin.message
    

    整个代码片段:

    apply plugin: 'com.android.application'
    apply plugin: MyCustomGradle
    
    class MyCustomGradle implements Plugin<Project>{
    
        @Override
        void apply(Project project) {
    
            project.extensions.create("myplugin", MyPluginExtension)
    
            project.task("customplugin"){
                doLast {
                    println "This is my custom plugin!" + project.myplugin.message;
                }
            }
        }
    }
    
    class MyPluginExtension{
        String message;
    }
    
    myplugin{
        message "Thank you!";
    }
    

    执行Task:

    D:\Android\Workspace\CustomGradlePlugin>gradlew "customplugin"
    NDK is missing a "platforms" directory.ompiling 
    ......
    
    :app:customplugin
    This is my custom plugin!Thank you!
    
    BUILD SUCCESSFUL
    
    Total time: 2.802 secs
    

    buildSrc project

    单独写Gradle Plugin文件,放在rootProjectDir/buildSrc/src/main/groovy/目录下,同一个工程中所有的构建文件都可以引用这个插件,但是不能被其他工程引用。

    新建一个名为buildSrc的工程,再构建如下目录,这里的buildSrc是多工程项目:

    buildSrc/build.gradle:

    buildscript {
        repositories {
            mavenCentral()
        }
    }
    

    my-gradle-plugin/build.gradle:

    apply plugin:'groovy'
    
    repositories {
        mavenCentral()
    }
    
    rootProject.dependencies{
        runtime project(path)
    }
    
    dependencies {
        compile localGroovy()
        compile gradleApi()
    }
    

    因为这里buildSrc底下有多个工程,所以要配置rootProject.dependencies{},同时还要设置仓库,不然编译的时候会找不到插件。官方文档的说法是:

    The buildSrc project can be a multi-project build, just like any other regular multi-project build. However, all of the projects that should be on the classpath of the actual build must be runtime dependencies of the root project in buildSrc.

    要在每个插件子工程的build.gradle中都配置runtime依赖。

    接着settings.gadle,如果是多项目这个文件一定不能忘记:

    include "plugins/my-gradle-plugin"
    

    myplugin.properties:

    implementation-class=com.example.plugin.AppPlugin
    

    这个文件的目的主要是设置插件的id,告诉Gradle这个插件的入口类在哪里。像上面那样配置之后我们就可以这样引入这个插件:

    apply plugin: 'myplugin'
    

    如果没有通过properties文件指定id,这样引入插件也行:

    apply plugin: com.example.plugin.AppPlugin.class
    

    之后在同工程的app中引用这个插件:

    apply plugin: 'com.android.application'
    apply plugin: 'myplugin'
    
    myplugin{
        message "buildSrc!"
    }
    

    运行Task:

    D:\Android\Workspace\MyBuildSrc>gradlew -q customplugin
    This is my custom plugin!buildSrc!
    

    采用buildSrc project这种方式来整合插件虽然看起来简单,不需要再配置classpath直接就能在其他子工程中引用,但是具体实现起来很麻烦,编译的时候常常提示找不到自定义的插件。有时候可能是因为在buildSrc在编译并添加到classpath之前子项目的build script就已经被编译且缓存了,这时候可以利用“gradlew --recompile-scripts”来强制重新编译。

    Standalone project

    这种方法把我们的插件代码移到一个独立的项目当中,这样我们就可以发布或者和别人分享我们的插件。这个项目简单来说就是构建一个JAR包,这个JAR包包含了我们在插件中所写的类。

    构建JAR包

    首先新建一个Android Library的module,名为“myplugin“。把myplugin的build.gradle文件的内容改为:

    apply plugin: 'groovy'
    apply plugin: 'maven'
    
    dependencies{
        compile gradleApi()//gradle sdk
        compile localGroovy()//groovy sdk
        compile fileTree(dir: 'libs', include: ['*.jar'])
    }
    
    uploadArchives {
        repositories{
            mavenDeployer {
                repository(url: uri(LOCAL_REPO_URL))
                pom.groupId = PROJ_GROUP
                pom.artifactId = PROJ_ARTIFACTID
                pom.version = PROJ_VERSION
            }
        }
    }
    

    uploadArchives就是打包成Task的配置。其中的配置常量可以写在gradle.properties文件中。设置了pom.groupId、pom.artifactId和pom.version之后,如果我们要引入这个插件,只要在buildscript{}的dependencies{}当中这样设置classpath即可:

    classpath 'pom.groupId : pom.artificatId : pom.version'
    

    在myplugin的根目录下新建文件gradle.properties:

    PROJ_NAME=myplugin
    
    PROJ_POM_NAME=Local Repository
    
    LOCAL_REPO_URL=D:/repos
    
    PROJ_GROUP=com.example.myplugin
    PROJ_ARTIFACTID=myplugin
    PROJ_VERSION=1.0.0
    PROJ_VERSION_CODE=1
    
    //项目描述
    PROJ_WEBSITEURL=http://kvh.io
    PROJ_ISSUETRACKERURL=https://github.com/kevinho/Embrace-Android-Studio-Demo/issues
    PROJ_VCSURL=https://github.com/kevinho/Embrace-Android-Studio-Demo.git
    PROJ_DESCRIPTION=demo apps for embracing android studio
    
    PROJ_LICENCE_NAME=The Apache Software License, Version 2.0
    PROJ_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
    PROJ_LICENCE_DEST=repo
    
    DEVELOPER_ID=your-dev-id
    DEVELOPER_NAME=your-dev-name
    DEVELOPER_EMAIL=your-email@your-mailbox.com
    

    这个文件主要配置一些常量。

    在main文件夹下面新建一个groovy文件夹和一个resources文件夹:

    在groovy文件夹下面新建com.example.myplugin的包,在包下面分别新建MyCustomGradle.groovy文件和MyPluginExtension.groovy文件。接着在resources文件夹下面新建名为META-INF的文件夹,再在META-INF下面新建gradle-plugins文件夹,在gradle-plugins文件夹下面新建com.example.myplugin.properties文件。在这个文件中写好入口类:

    //入口
    implementation-class = com.example.myplugin.MyCustomGradle
    

    这里com.example.myplugin.properties的命名需要注意,比如我命名为com.example.myplugin.properties,那我后面要在app中引入这个插件时就是这么写的:

    apply plugin: com.example.myplugin
    

    这样我的目录结构就成了这样:

    Android Studio中会把文件的后缀名隐藏起来,会导致编译出错或找不到自定义的插件,所以要手动补上去。尤其是properties文件和groovy文件。

    打开Android Studio右侧的Gradle,找到构建JAR包的Task,运行一下,就生成了我们想要的JAR包:

    打开我们指定的生成路径:

    在1.0.0文件夹中可以看到一个pom文件,里面就记录着我们在前面配置的信息:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.example.myplugin</groupId>
      <artifactId>myplugin</artifactId>
      <version>1.0.0</version>
    </project>
    
    

    在项目中使用自定义插件

    首先要在根项目的build.gradle文件中设置好仓库和依赖:

    buildscript {
        repositories {
            maven{
                url uri(LOCAL_REPO_URL)
            }
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.3.3'
            classpath 'com.example.myplugin:myplugin:1.0.0'
        }
    }
    

    然后在app module的build.gadle文件中引入插件,并配置好信息:

    apply plugin: 'com.android.application'
    apply plugin: 'com.example.myplugin'
    
    myplugin{
        message "Standlone Project!";
    }
    ......
    

    然后执行这个Task:

    D:\Android\Workspace\CustomGradlePlugin>gradlew -p app customplugin
    NDK is missing a "platforms" directory.
    If you are using NDK, verify the ndk.dir is set to a valid NDK directory.  It is currently set to D:\Android\sdk\ndk-bundle.
    If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
    
    Incremental java compilation is an incubating feature.
    :app:customplugin
    This is my custom plugin!Standlone Project!
    
    BUILD SUCCESSFUL
    
    Total time: 2.758 secs
    

    相关文章

      网友评论

          本文标题:自定义Gradle插件

          本文链接:https://www.haomeiwen.com/subject/mvxhrxtx.html