欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解如何使用Android Studio开发Gradle插件

程序员文章站 2023-11-23 20:55:58
缘由 首先说明一下为什么会有这篇文章。前段时间,插件化以及热修复的技术很热,nuwa热修复的工具nuwagradle,携程动态加载技术dynamicapk,还有希望做最轻...

缘由

首先说明一下为什么会有这篇文章。前段时间,插件化以及热修复的技术很热,nuwa热修复的工具nuwagradle,携程动态加载技术dynamicapk,还有希望做最轻巧的插件化框架的small。这三个app有一个共同的地方就是大量的使用了gradle这个强大的构建工具,除了携程的框架外,另外两个都发布了独立的gradle插件提供自动化构建插件,或者生成热修复的补丁。所以学习一下gradle插件的编写还是一件十分有意义的事。

插件类型

gradle的插件一般有这么几种:

  1. 一种是直接在项目中的gradle文件里编写,这种方式的缺点是无法复用插件代码,在其他项目中还得复制一遍代码(或者说说复制一遍文件)
  2. 另一种是在独立的项目里编写插件,然后发布到*仓库,之后直接引用就可以了,优点就是可复用。就和上面的nuwa和small一样。

gradle相关语法

本篇文章不会详细说明gradle相关的语法,如果要学习gradle相关的东西,请查看gradle for android

gradle插件开发

gradle插件是使用groovy进行开发的,而groovy其实是可以兼容java的。android studio其实除了开发android app外,完全可以胜任开发gradle插件这一工作,下面来讲讲具体如何开发。

  1. 首先,新建一个android项目。
  2. 之后,新建一个android module项目,类型选择android library。
  3. 将新建的module中除了build.gradle文件外的其余文件全都删除,然后删除build.gradle文件中的所有内容。
  4. 在新建的module中新建文件夹src,接着在src文件目录下新建main文件夹,在main目录下新建groovy目录,这时候groovy文件夹会被android识别为groovy源码目录。除了在main目录下新建groovy目录外,你还要在main目录下新建resources目录,同理resources目录会被自动识别为资源文件夹。在groovy目录下新建项目包名,就像java包名那样。resources目录下新建文件夹meta-inf,meta-inf文件夹下新建gradle-plugins文件夹。这样,就完成了gradle 插件的项目的整体搭建,之后就是小细节了。目前,项目的结构是这样的。

详解如何使用Android Studio开发Gradle插件

打开module下的build.gradle文件,输入

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

dependencies {
  compile gradleapi()
  compile localgroovy()
}

repositories {
  mavencentral()
}

下面我们在包名下新建一个文件,命名为pluginimpl.groovy,注意有groovy后缀,然后在里面输入,注意包名替换为你自己的包名。

package cn.edu.zafu.gradle

import org.gradle.api.plugin
import org.gradle.api.project

public class pluginimpl implements plugin<project> {
  void apply(project project) {
    project.task('testtask') << {
      println "hello gradle plugin"
    }
  }
}

然后在resources/meta-inf/gradle-plugins目录下新建一个properties文件,注意该文件的命名就是你只有使用插件的名字,这里命名为plugin.test.properties,在里面输入

implementation-class=cn.edu.zafu.gradle.pluginimpl

注意包名需要替换为你自己的包名。

这样就完成了最简单的一个gradle插件,里面有一个叫testtask的task,执行该task后会输出一段文字,就像当初我们输出helloworld一样。

发布到本地仓库

接着,我们需要将插件发布到maven*仓库,我们将插件发布到本地仓库就好了,在module项目下的buidl.gradle文件中加入发布的代码。

repositories {
  mavencentral()
}
group='cn.edu.zafu.gradle.plugin'
version='1.0.0'

uploadarchives {
  repositories {
    mavendeployer {
      repository(url: uri('../repo'))
    }
  }
}

上面的group和version的定义会被使用,作为maven库的坐标的一部分,group会被作为坐标的groupid,version会被作为坐标的version,而坐标的artifactid组成即module名,我们让其取一个别名modulename。然后maven本地仓库的目录就是当前项目目录下的repo目录。

这时候,右侧的gradle toolbar就会在module下多出一个task

详解如何使用Android Studio开发Gradle插件

点击uploadarchives这个task,就会在项目下多出一个repo目录,里面存着这个gradle插件。

详解如何使用Android Studio开发Gradle插件

目录就像上图这样,具体目录结构和你的包名等一系列有关,time是我的module名。

发布到本地maven仓库后,我们就使用它,在叫app的android项目下的gradle.build的文件中加入

buildscript {
  repositories {
    maven {
      url uri('../repo')
    }
  }
  dependencies {
    classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
  }
}
apply plugin: 'plugin.test'

apply plugin后面引号内的名字就是前文plugin.test.properties文件的文件名。而class path后面引号里的内容,就是上面grade中定义的group,version以及modulename所共同决定的,和maven是一样的。

同步一下gradle,右侧app下other分类下就会多出一个testtask,双击执行这个task,控制台就会输出刚才我们输入的字符串

详解如何使用Android Studio开发Gradle插件

发布到jcenter仓库

接下来我们将其发布到jcenter*仓库。

在项目根目录下的build.gradle文件中加入。

dependencies {
    classpath 'com.android.tools.build:gradle:2.0.0-beta6'
    classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
    classpath 'com.github.dcendents:android-maven-plugin:1.2'
  }

在项目根路径下新建bintray.gradle文件,输入

apply plugin: 'com.jfrog.bintray'
apply plugin: 'maven-publish'

def projectname = "timeplugin"
def mavendesc = 'your desc'
def baseurl = 'https://github.com/yourbaseurl'
def siteurl = baseurl
def giturl = "${baseurl}/yourgiturl"
def issueurl = "${baseurl}/yourgitissueurl"

def licenseids = ['apache-2.0']
def licensenames = ['the apache software license, version 2.0']
def licenseurls = ['http://www.apache.org/licenses/license-2.0.txt']
def inception = '2016'

def username = 'lizhangqu'

install {
  repositories {
    maveninstaller {
      pom.project {
        // description
        name projectname
        description mavendesc
        url siteurl

        // archive
        groupid project.group
        artifactid archivesbasename
        version project.version

        // license
        inceptionyear inception
        licenses {
          licensenames.eachwithindex { ln, li ->
            license {
              name ln
              url licenseurls[li]
            }
          }
        }
        developers {
          developer {
            name username
          }
        }
        scm {
          connection giturl
          developerconnection giturl
          url siteurl
        }
      }
    }
  }
}

task sourcesjar(type: jar) {
  from sourcesets.main.allgroovy
  classifier = 'sources'
}


task javadocjar(type: jar, dependson: groovydoc) {
  from groovydoc.destinationdir
  classifier = 'javadoc'
}

artifacts {
  archives javadocjar
  archives sourcesjar
}


bintray {
  user = bintray_user
  key = bintray_key
  configurations = ['archives']
  pkg {
    repo = 'maven'
    name = projectname
    desc = mavendesc
    websiteurl = siteurl
    issuetrackerurl = issueurl
    vcsurl = giturl
    labels = ['gradle', 'plugin', 'time']
    licenses = licenseids
    publish = true
    publicdownloadnumbers = true
  }
}

将对应的描述性文字修改为你自己的信息,尤其是最前面的一系列的def定义,然后在gradle.properties文件中加入bintray_user和bintray_key。

在你的module中apply该grade文件

apply from: '../bintray.gradle'

右侧的gradle的toolbar就会多出几个task

详解如何使用Android Studio开发Gradle插件

之后我们先运行other下的install这个task,再执行bintrayupload这个task,如果不出意外,就上传了,之后不要忘记到后台add to jcenter。成功add到jcenter之后就会有link to jcenter的字样

详解如何使用Android Studio开发Gradle插件

耐心等待add to center成功的消息,之后就可以直接引用了,将module下的gradle文件maven部分的定义

    maven {
      url uri('../repo')
    }

前面加入

jcenter()

最终的内容如下

buildscript {
  repositories {
    jcenter()
    maven {
      url uri('../repo')
    }
  }
  dependencies {
    classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
  }
}

apply plugin: 'plugin.test'

就是这么简单,再次运行一下测试下是否成功。

最佳实践

最佳实践的来源是源自multidex,为什么呢,因为最近当方法数超了之后,如果选择multidex,编译的过程就会慢很多很多,为了检测到底是哪一步的耗时,需要编写一个插件来统计各个task执行的时间,因此就有了这么一个最佳实践。

在pluginimpl同级目录下新建timelistener.groovy文件。输入

package cn.edu.zafu.gradle

import org.gradle.buildlistener
import org.gradle.buildresult
import org.gradle.api.task
import org.gradle.api.execution.taskexecutionlistener
import org.gradle.api.initialization.settings
import org.gradle.api.invocation.gradle
import org.gradle.api.tasks.taskstate
import org.gradle.util.clock

class timelistener implements taskexecutionlistener, buildlistener {
  private clock clock
  private times = []

  @override
  void beforeexecute(task task) {
    clock = new org.gradle.util.clock()
  }

  @override
  void afterexecute(task task, taskstate taskstate) {
    def ms = clock.timeinms
    times.add([ms, task.path])
    task.project.logger.warn "${task.path} spend ${ms}ms"
  }

  @override
  void buildfinished(buildresult result) {
    println "task spend time:"
    for (time in times) {
      if (time[0] >= 50) {
        printf "%7sms %s\n", time
      }
    }
  }

  @override
  void buildstarted(gradle gradle) {}

  @override
  void projectsevaluated(gradle gradle) {}

  @override
  void projectsloaded(gradle gradle) {}

  @override
  void settingsevaluated(settings settings) {}
}

然后将pluginimpl文件中的apply方法修改为

void apply(project project) {
    project.gradle.addlistener(new timelistener())
  }

完成后打包发布到jcenter()。之后你只要引用了该插件,就会统计各个task执行的时间,比如运行app,就会输出像下面的信息。

详解如何使用Android Studio开发Gradle插件

最佳实践的末尾,推广一下这个插件,这个插件我已经将其发布到jcenter仓库,如果要使用的话加入下面的代码即可

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
  }
}

apply plugin: 'plugin.time'

传递参数

上面的是小试牛刀了下,接下来我们需要获得自定义的参数。

首先按照上面的步骤新建一个module。新建pluginextension.groovy,输入

public class pluginextension {
  def param1 = "param1 defaut"
  def param2 = "param2 defaut"
  def param3 = "param3 defaut"
}

然后我们希望能传入嵌套的参数,再新建一个pluginnestextension.groovy,输入

public class pluginnestextension {
  def nestparam1 = "nestparam1 defaut"
  def nestparam2 = "nestparam2 defaut"
  def nestparam3 = "nestparam3 defaut"
}

然后新建一个customtask.groovy,继承defaulttask类,使用 @taskaction注解标注实现的方法

public class customtask extends defaulttask {

  @taskaction
  void output() {
    println "param1 is ${project.pluginext.param1}"
    println "param2 is ${project.pluginext.param2}"
    println "param3 is ${project.pluginext.param3}"
    println "nestparam1 is ${project.pluginext.nestext.nestparam1}"
    println "nestparam2 is ${project.pluginext.nestext.nestparam2}"
    println "nestparam3 is ${project.pluginext.nestext.nestparam3}"
  }
}

只是做了拿到了参数,然后做最简单的输出操作,使用 ${project.pluginext.param1}和 ${project.pluginext.nestext.nestparam1}等拿到外部的参数。

别忘了在meta-inf/gradle-plugins目录下新建properties文件指定插件的接口实现类。

复制之前新建的pluginimpl.groovy到包下,修改apply方法

public class pluginimpl implements plugin<project> {
  void apply(project project) {
    project.extensions.create('pluginext', pluginextension)
    project.pluginext.extensions.create('nestext', pluginnestextension)
    project.task('customtask', type: customtask)
  }
}

将插件发布到本地maven后,进行引用。

buildscript {
  repositories {
    maven {
      url uri('../repo')
    }
  }
  dependencies {
    classpath 'cn.edu.zafu.gradle.plugin:test:1.0.0'
  }
}
apply plugin: 'plugin.test'

定义外部参数,这里我们定义了param1,param2,nestparam1,nestparam2,此外param3和nestparam3保持默认。

pluginext {
  param1 = 'app param1'
  param2 = 'app param2'
  nestext{
    nestparam1='app nestparam1'
    nestparam2='app nestparam2'

  }
}

同步一下gradle,执行customtask。

详解如何使用Android Studio开发Gradle插件

上面的代码很简单,不用解释也能看到,所以不再解释了。

 源码

最后上本篇文章的源码 :gradleplugin_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。