gradle插件开发

记得我以前曾经发布过一篇发布类库到jCenter的文章:Android开发发布lib到jcenter,发布成功之后,只需要在gradle写一行compile…就可以把类库导进来。当然,你如果觉得麻烦也可以使用第三方的工具,如jitpack,可以很方便把你在GitHub上的项目发布为类库。
好了,言归正传,今天主要说一下和发布类库很相似的发布gradle插件教程,你可能见过很多项目除了通过compile…导进,还有不少是这样的,比如greendao:

// In your root build.gradle file:
buildscript {
    repositories {
        jcenter()
        mavenCentral() // add repository
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
    }
}
 
// In your app projects build.gradle file:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
 
dependencies {
    implementation 'org.greenrobot:greendao:3.2.2' // add library
}

在讲gradle插件开发之前,先了解一下Gradle插件开发支持Java、Groovy、Scala三种语言开发,Groovy用于实现与 Gradle 构建生命周期(如 task 的依赖)有关的逻辑,Java用于核心逻辑,表现为 Groovy 调用 Java 的代码。我们平时所使用的只有一行compile…的类库主要是用Java开发的实现功能的模块,之所以会出现稍复杂的插件开发,是因为gradle插件可以很好地控制构建项目的生命周期,如task的依赖等。

我们如果想在一个AndroidStudio项目导入一个jar/aar有好几种方式:
① 直接放到lib目录,配合
implementation fileTree(include: ['*.jar'], dir: 'libs')
如果是aar,可能还需要在build.gradle(app)的android{}括号里添加

repositories {
     flatDir {
         dirs 'libs'
     }
 }

dependencies{}括号里添加

implementation (name:'aar文件名', ext:'aar')

② File>New>New Module -> Import .JAR/.AAR Package,导入你想添加的jar或aar文件成为一个module,然后在你项目添加该module为依赖即可。

③ 直接在builde.gradle(app)里写代码

def getSDKPlatformPath() {
    def rootDir = project.rootDir
    def localProperties = new File(rootDir, "local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream {
            instr -> properties.load(instr)
        }
        // "${android.getSdkDirectory().getAbsolutePath()}"
        def sdkDir = properties.getProperty('sdk.dir')
        //def compileSdkVersion = android.compileSdkVersion
        // 这里使用17的来编译(Android 4.2)
        def compileSdkVersion = "android-17"
        Console.println("app compileSdkVersion : " + compileSdkVersion)
        def path = sdkDir + "/platforms/" + compileSdkVersion
        return path
    }
    return rootDir
}

// 导入layoutlib.jar
def getLayoutlibJarPath() {
    def path = getSDKPlatformPath()
    def layoutlibPath = path + "/data/layoutlib.jar"
    Console.println("layoutlib-path : " + layoutlibPath)
    return layoutlibPath
}

// 导入android.jar
def getAndroidJar() {
    def path = getSDKPlatformPath()
    def androidPath = "${path}${File.separator}android.jar"
    return androidPath
}

然后在dependencies{}里添加:

    // 方式①:使用到了一些@hide的类库和方法,如删除缓存的IPackageDataObserver
    //provided files(getLayoutlibJarPath())

以上做法,都是比较简单的:
方式①没有明显的缺点,compile/implementation语句虽然会增加,但尚在可以接收的范围之内,需要拷贝jar/aar到项目lib文件夹下,如果依赖仅在本项目使用,建议使用此方式;
方式②有点小题大作了,我本想导入一个jar包,结果要做成一个module;
方式③太臃肿,如果需要做的逻辑越来越多,那么gradle文件里的代码也越来越多,这样肯定是不行的。

于是,针对需要公开为类库,且需要控制项目构建声明周期(环境配置)的情况,我们引入了gradle插件开发。

  1. 新建一个java library module
    在这里插入图片描述
  2. 切换到Project视图,把main文件夹下的java文件夹命名为groovy
    在这里插入图片描述
  3. 在main目录下,新建resources文件夹,然后新建META-INF文件夹,然后新建gradle-plugin文件夹,最后新建hide_api.properties文件,这个hide_api就是插件命,稍后要用到。
    注意:
    groovy文件夹中的类,一定要修改成 .groovy 后缀,IDE才会正常识别。
    resources/META-INF/gradle-plugins 这个文件夹结构是强制要求的,否则不能识别成插件
├── build.gradle
└── src
    └── main
        ├── groovy
        │   └── com.smartdevice.dtv.tvmanager.hidelib
        │               ├── HideApiPlugin.groovy
        │             
        └── resources
            └── META-INF
                └── gradle-plugins
                    └── hide_api.properties

build.gradle内容:

apply plugin: 'groovy'
apply plugin: 'maven'

repositories {
    mavenLocal()
    jcenter()
}

dependencies {
    compile gradleApi()
}

//发布到本地目录
def versionName = "1.0.0"
group "com.itant.hide.plugin"
version versionName

uploadArchives{
//当前项目可以发布到本地文件夹中
    repositories {
        mavenDeployer {
            repository(url: uri('./repo')) //定义本地maven仓库的地址
        }
    }
}

HideApiPlugin.groovy内容:

package com.smartdevice.dtv.tvmanager.hidelib

import org.gradle.api.Project
import org.gradle.api.Plugin

public class HideApiPlugin implements Plugin<Project>{
    private Project mProject

    @Override
    void apply(Project project) {
        mProject = project
        initAndroidSDK(project)
    }

    private void initAndroidSDK(Project project) {
        // 导入D:\software\assdk\android-sdk-windows\platforms\android-17\data\layoutlib.jar
        // 这样我们就可以在开发的过程中使用很多@hide的方法了,注意,我们仅仅需要编译时使用,所以使用插件的时候
        // 需要通过provided files(gradle.ext.layoutlibJar)导入
        project.rootProject.gradle.ext.layoutlibJar = getLayoutlibJar()
    }

    private String getLayoutlibJar() {
        def path = getSDKPath()
        //def layoutlibPath = "${path}${File.separator}data${File.separator}layoutlib.jar"
        def layoutlibPath = "${path}${File.separator}data${File.separator}layoutlib.jar"
        return layoutlibPath
    }

    /*private String getSDKPath() {
        def path = "${android.getSdkDirectory().getAbsolutePath()}";
        return path
    }*/

    private String getSDKPath() {
        def android_home = System.getenv()['ANDROID_HOME']
        if (android_home == null || android_home.equals("")) {
            android_home = getSDKPathFromLocalProperty()
        }
        if (android_home == null || android_home.equals(""))
            return 'not found android.jar'
        //def compileSdkVersion = 'android-' + API_LEVEL
        def compileSdkVersion = 'android-17'
        def path = "${android_home}${File.separator}platforms${File.separator}${compileSdkVersion}"
        return path
    }

    private String getSDKPathFromLocalProperty() {
        def rootDir = mProject.rootDir
        def localProperties = new File(rootDir, "local.properties")
        if (localProperties.exists()) {
            Properties properties = new Properties()
            localProperties.withInputStream {
                instr -> properties.load(instr)
            }
            return properties.getProperty('sdk.dir')
        }
        return ""
    }
}

hide_api.properties内容:

implementation-class=com.smartdevice.dtv.tvmanager.hidelib.HideApiPlugin
  1. 发布插件到本地:
    terminal中执行:
gradlew uploadArchives

成功之后,会生成repo目录:
在这里插入图片描述

  1. 此时可以在app主项目使用我们的插件了。
    build.gradle(Project)中的buildscript{}里添加:
repositories {
        google()
        jcenter()
        maven{
            url '/hidelib/repo/'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // 这里与我们上面生成的repo文件夹下的maven-metadata.xml对应,不要写错了
        classpath "com.itant.hide.plugin:hidelib:1.0.0"
    }

在build.gradle(app)里顶部使用我们开发的插件,hide_api就是我们上面定义的文件名,不能写错了:

apply plugin: 'hide_api'

最后,在build.gradle(app)的dependencies{}里添加

// 使用gradle插件形式,不需要依赖module,“ext.layoutlibJar”只是我们在HideApiPlugin.groovy里定义的一个变量名称
provided files(gradle.ext.layoutlibJar)

就可以使用我们开发的插件,并且导进了layoutlib类库了,不需要在File–>Project Structure–>dependencies里添加对该module的引用。

注意:
如果开发过程中,你修改了groovy文件,或者module里的配置文件,需要先删除repo文件夹,重新执行gradlew uploadArchives命令生成新的repo文件。
参考:
https://www.jianshu.com/p/3191c3955194
https://www.jianshu.com/p/3c59eded8155

Guess you like

Origin blog.csdn.net/ithouse/article/details/86353862