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

缘由

首先说明一下为什么会有这篇文章。前段时间,插件化以及热修复的技术很热,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 插件的项目的整体搭建,之后就是小细节了。目前,项目的结构是这样的。

打开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

点击uploadArchives这个Task,就会在项目下多出一个repo目录,里面存着这个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,控制台就会输出刚才我们输入的字符串

发布到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

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

耐心等待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,就会输出像下面的信息。

最佳实践的末尾,推广一下这个插件,这个插件我已经将其发布到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。

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

 源码

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

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

(0)

相关推荐

  • Android Studio使用教程(五):Gradle命令详解和导入第三方包

    Android Studio + Gradle的组合用起来非常方便,很多第三方开源项目也早都迁移到了Studio,为此今天就来介绍下查看.编译并导入第三方开源项目的方法. Sublime + Terminal编译并查看源码 首先来给大家介绍一种简便并且个人最喜欢的一种办法.很多时候我们在GitHub上看到一个不错的开源项目,一般有两种需求,阅读源码和查看运行效果,如果是单纯的查看源码我更喜欢用一些轻量级编辑器,如vim,sublime等,vim不是很熟练,所以个人一种都习惯用sublime来查看

  • Android Studio使用教程(六):Gradle多渠道打包

    由于国内Android市场众多渠道,为了统计每个渠道的下载及其它数据统计,就需要我们针对每个渠道单独打包,如果让你打几十个市场的包岂不烦死了,不过有了Gradle,这再也不是事了. 友盟多渠道打包 废话不多说,以友盟统计为例,在AndroidManifest.xml里面会有这么一段: 复制代码 代码如下: <meta-data     android:name="UMENG_CHANNEL"     android:value="Channel_ID" /&g

  • AndroidStudio Gradle第三依赖统一管理的实现方法

    AndroidStudio由于使用了gradle的进行项目构建,使我们开发app方便很多,今天我就给大家列出几点是用gradle的方便之处. 一.AndroidStudio Gradle第三依赖统一管理 二.AndroidStudio Gradle基于友盟的多渠道打包 三.AndroidStudio安全管理签名文件keystroe和签名密码 这三篇文章很好的讲解了gradle的在打包和项目依赖管理的优点,大家可以参考一下,来提高自己的开发效率,增强签名文件的安全性. 在很多时候我们使用Andro

  • Android Studio使用教程(四):Gradle基础

    其实很早之前也写了一篇Gradle的基础博客,但是时间很久了,现在Gradle已经更新了很多,所以暂且结合Stduio 1.0正式版与最新的Gradle语法来详细讲解下,小伙伴们直接跟我一步步来学习吧. 什么是Gradle? Gradle是一种依赖管理工具,基于Groovy语言,面向Java应用为主,它抛弃了基于XML的各种繁琐配置,取而代之的是一种基于Groovy的内部领域特定(DSL)语言. 安装Gradle 在Android Studio系列教程一–下载与安装中新建项目成功后会下载Grad

  • AndroidStudio Gradle基于友盟的多渠道打包方法

    AndroidStudio由于使用了gradle的进行项目构建,使我们开发app方便很多,今天我就给大家列出几点是用gradle的方便之处. 一.AndroidStudio Gradle第三依赖统一管理 二.AndroidStudio Gradle基于友盟的多渠道打包 三.AndroidStudio安全管理签名文件keystroe和签名密码 好久没写博客了,今天写一篇关于多渠道打包的文章. 首先我们要下载友盟的jar包,然后照着文档配置即可.(这里就不过多讲解了,直接看我的代码) 文档地址:ht

  • 为Android Studio编写自定义Gradle插件的教程

    Google已经建议Android开发全部转向Android Studio开发,Android Studio 是使用gradle编译.打包的,那么问题来了,gradle可是有一堆东西...,为了彻底了解gradle,今天就来学习下如何写自己的gradle插件(当然插件源码是使用groovy写的),先看如下代码目录: 如上图所示,plugin目录是插件源码目录,sample是用来测试插件的. 1.在目录plugin/src/main/groovy/com/micky/gradle/下新建插件类My

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

    缘由 首先说明一下为什么会有这篇文章.前段时间,插件化以及热修复的技术很热,Nuwa热修复的工具NuwaGradle,携程动态加载技术DynamicAPK,还有希望做最轻巧的插件化框架的Small.这三个App有一个共同的地方就是大量的使用了Gradle这个强大的构建工具,除了携程的框架外,另外两个都发布了独立的Gradle插件提供自动化构建插件,或者生成热修复的补丁.所以学习一下Gradle插件的编写还是一件十分有意义的事. 插件类型 Gradle的插件一般有这么几种: 一种是直接在项目中的g

  • 详解如何在Android studio中更新sdk版本和build-tools版本

    一.首先看下Android开发用到的sdk目录: build-tools 保存着一些Android平台相关通用工具,比如adb.和aapt.aidl.dx等文件.  aapt即Android Asset Packaging Tool , 在SDK的build-tools目录下. 该工具可以查看, 创建, 更新ZIP格式的文档附件(zip, jar, apk). 也可将资源文件编译成二进制文件.  Adb 即android debug bridge 管理模拟器和真机的万能工具,ddms 调试环境 

  • Android Studio调试Gradle插件详情

    前言: 使用ASM在编译期修改字节码来完成各种功能(统计方法耗时.全埋点统计...),就需要开发Gradle插件.开发插件中遇到问题排查使用日志输出效率太低了,能断点调试的话就更好了.其实已经有很多文章写了调试插件的方法,但是自己照着他们的步骤下来还是遇到一些问题,把这些问题记录下来方便大家少走弯路. Android Studio版本是3.5.2,Gradle版本是3.5.1 1.点击[Run]->[Edit Configurations...]: 2.然后点击左上角的[+],选择[Remote

  • 详解如何使用Android Studio 进行NDK开发和调试

    尽管Android Studio已经越来越流行了,但很多人还是习惯于Eclipse或源码环境下开发JNI应用.个人认为使用Android Studio作NDK开发是必然趋势,所以本文将简单介绍如何在Android Studio上实现NDK开发. 简介 JNI JNI 是Java Native Inteface的缩写,是Java中定义的一种用于连接Java和C/C++接口的一种实现方式. NDK NDK 是 Native Developmentit的缩写,是Google在Android开发中提供的

  • 详解如何在Android Studio中添加RecyclerView-v7支持包

    一直知道RecyclerView可以代替ListView.GridView使用,听说功能很强大,但还没有去学习过.今天想学习,没想到还没开始便撞墙了.输入Recycler,只有这两个东西,没有提示RecyclerView,说明支持包中没有. 最后一番百度后,终于解决(真不敢想象没有网络的情况下,怎么开发.怎么解决问题). 1.打开SDK Manager,在Extras树下找到Android Support Library,下载好支持包.RecyclerView在v7-21版本就出来了.我这里不用

  • Android Studio开发环境搭建教程详解

    对于移动端这块,笔者之前一直都是进行iOS开发的,也从来没用过Java.但是因为进入了Google Android全国大学生移动互联网创新挑战赛(进入官网)的总决赛(笔者"西部计算机教育提升计划"的项目被直接推荐进入决赛),这个比赛要求一定要提交apk程序,所以我不得不赶紧学习一下Android开发了. 下面就对自己学习的过程做一个记录. 一.安装Android Studio 笔者用的计算机配置如下: Mac下安装Android Studio应该更简单一些,只需要下载一个Android

  • Android studio利用gradle打jar包并混淆的方法详解

    本文主要介绍了Android studio利用gradle打jar包并混淆的方法,下面话不多说,来看看详细的介绍吧. 首先打jar包的配置很简单,使用jar的task,可以参考gradle官方文档,具体代码如下: task buildJar(type: Jar, dependsOn: ['assembleRelease']) { destinationDir = file('build/outputs/jar/') appendix = "" baseName = "&quo

  • Android Studio中ButterKnife插件的安装与使用详解

    1>Android Studio 安装ButterKnife插件 同安装其他插件类似,如下: 1.1>打开Plugins界面 按照上图中1,2,3指示操作(注意:这里我的Android Studio中已经安装了该插件,所以显示的内容不太一样).然后重启Android Studio. 2>在项目上使用该开源项目(以Android Studio 为例) 2.1>在bulid.gradle中添加依赖 重新编译一下该项目,通过后继续操作. 2.2>在代码中就可以使用注解的方式了 2.

  • Android Studio和Gradle使用不同位置JDK的问题解决

    初次安装Android Studio,遇到了不少问题,这是其中的一个,分享如下,同时求各位dalao关注一下啦((*^__^*) ) 使用不同的JDK位置可能会导致Gradle产生多个守护进程 ,首先Android Studio默认下使用的下载时自带的jre,这种jre的功能显然达不到jdk的标准,这会使得开发时可能产生不兼容的问题,当然也可以选择忽略这个警告,但是将来也可能出现问题.因此还是解决掉为好. 注意的问题:下载Android Studio一定是要下载JDK的,如果原先电脑里下载了JA

  • android studio更新gradle错误构建项目失败的解决方法

    一.版本错误 对应版本,修改gradle version,和plusing version两个地方修改gradle version,和plusing version的方法有两种,一种是在 project.build.这里plusing version的设置 buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.0' } } allprojects

随机推荐