欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 运维知识 > Android >内容正文

Android

拥抱 Android Studio 之五:Gradle 插件开发

发布时间:2025/3/15 Android 39 豆豆
生活随笔 收集整理的这篇文章主要介绍了 拥抱 Android Studio 之五:Gradle 插件开发 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

实践出真知

笔者有位朋友。每次新学一门语言,都会用来写一个贪吃蛇游戏,以此来检验自己学习的成果。笔者也有相似体会。所谓纸上得来终觉浅,绝知此事要躬行。这一章。笔者将以开发和公布一个 Gradle 插件作为目标,加深学习成果。

官方文档给出了比較具体的实现步骤,本文的脉络会跟官方文档差不了太多。额外增补实际样例和一些实践经验。文中的代码已经托管到了 github 项目中。

需求

默认的 Android 打包插件会把 apk 命名成 module-productFlavor-buildType.apk,比如 app-official-debug.apk,而且会把包文件公布到固定的位置: module/build/outputs/apk 有的时候,这个命名风格并非你所要的,你也想讲 apk 输出到别的目录。咱们通过 gradle 插件来实现自己定义。这个插件的需求是:

  • 输入一个名为 nameMap 的 Closure。用来改动 apk 名字
  • 输入一个名为 destDir 的 String,用于输出位置

原理简述

插件之于 Gradle

依据官方文档定义,插件打包了可重用的构建逻辑。能够适用于不同的项目和构建过程。

Gradle 提供了非常多官方插件。用于支持 Java、Groovy 等工程的构建和打包。同一时候也提供了自己定义插件的机制,让每个人都能够通过插件来实现特定的构建逻辑,并能够把这些逻辑打包起来。分享给其它人。

插件的源代码能够使用 Groovy、Scala、Java 三种语言,笔者不会 Scala,所以平时仅仅是使用 Groovy 和 Java。前者用于实现与 Gradle 构建生命周期(如 task 的依赖)有关的逻辑,后者用于核心逻辑。表现为 Groovy 调用 Java 的代码。

另外,还有非常多项目使用 Eclipse 或者 Maven 进行开发构建,用 Java 实现核心业务代码,将有利于实现高速迁移。

插件打包方式

Gradle 的插件有三种打包方式。主要是依照复杂程度和可见性来划分:

Build script

把插件写在 build.gradle 文件里,一般用于简单的逻辑,仅仅在该 build.gradle 文件里可见,笔者经常使用来做原型调试。本文将简要介绍此类。

buildSrc 项目

将插件源代码放在 rootProjectDir/buildSrc/src/main/groovy 中,仅仅对该项目中可见,适用于逻辑较为复杂,但又不须要外部可见的插件,本文不介绍,有兴趣能够參考此处。

独立项目

一个独立的 Groovy 和 Java 项目,能够把这个项目打包成 Jar 文件包,一个 Jar 文件包还能够包括多个插件入口,将文件包公布到托管平台上,供其它人使用。本文将着重介绍此类。

Build script 插件

首先来直接在 build.gradle 中写一个 plugin:

class ApkDistPlugin implements Plugin<Project> {@Overridevoid apply(Project project) {project.task('apkdist') << {println 'hello, world!'}} }apply plugin: ApkDistPlugin

命令行执行

$ ./gradlew -p app/ apkdist :app:apkdist hello, world!

这个插件创建了一个名为 apkdist 的 task。并在 task 中打印。

插件是一个类,继承自 org.gradle.api.Plugin 接口,重载 void apply(Project project) 方法。这种方法将会传入使用这个插件的 project 的实例。这是一个重要的 context。

接受外部參数

通常情况下,插件使用方须要传入一些配置參数,如 bugtags 的 SDK 的插件须要接受两个參数:

bugtags {appKey "APP_KEY" //这里是你的 appKeyappSecret "APP_SECRET" //这里是你的 appSecret。管理员在设置页能够查看 }

相同,ApkDistPlugin 这个 plugin 也希望接受两个參数:

apkdistconf {nameMap { name ->println 'hello,' + namereturn name}destDir 'your-distribution-dir' }

參数的内容后面继续完好。那这两个參数怎么传到插件内呢?

org.gradle.api.Project 有一个 ExtensionContainer getExtensions() 方法。能够用来实现这个传递。

声明參数类

声明一个 Groovy 类,有两个默认值为 null 的成员变量:

class ApkDistExtension {Closure nameMap = null;String destDir = null; }

接受參数

project.extensions.create('apkdistconf', ApkDistExtension);

要注意,create 方法的第一个參数就是你在 build.gradle 文件里的进行參数配置的 dsl 的名字,必须一致。第二个參数。就是參数类的名字。

获取和使用參数

在 create 了 extension 之后,假设传入了參数,则会携带在 project 实例中,

def closure = project['apkdistconf'].nameMap; closure('wow!');println project['apkdistconf'].destDir

进化版本号一:參数

class ApkDistExtension {Closure nameMap = null;String destDir = null; }class ApkDistPlugin implements Plugin<Project> {@Overridevoid apply(Project project) {project.extensions.create('apkdistconf', ApkDistExtension);project.task('apkdist') << {println 'hello, world!'def closure = project['apkdistconf'].nameMap;closure('wow!');println project['apkdistconf'].destDir}} }apply plugin: ApkDistPluginapkdistconf {nameMap { name ->println 'hello, ' + namereturn name}destDir 'your-distribution-directory' }

执行结果:

$ ./gradlew -p app/ apkdist :app:apkdist hello, world! hello, wow! your-distribution-directory

独立项目插件

代码写到如今,已经不适合再放在一个 build.gradle 文件里面了,那也不是我们的目的。

建立一个独立项目,把代码搬到相应的地方。

理论上,IntelliJ IDEA 开发插件要比 Android Studio 要方便一点点,由于有相应 Groovy module 的模板。

但事实上假设我们了解 IDEA 的项目文件结构。就不会受到这个局限,无非就是一个 build.gradle 构建文件加 src 源代码目录。

最终项目的目录结构是这样:

以下我们来一步步解说。

创建项目

在 Android Studio 中新建 Java Library module “plugin”。

改动 build.gradle 文件

加入 Groovy 插件和相应的两个依赖。

//removed java plugin apply plugin: 'groovy'dependencies {compile gradleApi()//gradle sdkcompile localGroovy()//groovy sdkcompile fileTree(dir: 'libs', include: ['*.jar']) }

改动项目目录

src/main 项目文件下:

  • 移除 java 目录,由于在这个项目中用不到 java 代码
  • 加入 groovy 目录,基本的代码文件放在这里
  • 加入 resources 目录,存放用于标识 gradle 插件的 meta-data

建立相应文件

. ├── build.gradle ├── libs ├── plugin.iml └── src└── main├── groovy│ └── com│ └── asgradle│ └── plugin│ ├── ApkDistExtension.groovy│ └── ApkDistPlugin.groovy└── resources└── META-INF└── gradle-plugins└── com.asgradle.apkdist.properties

注意:

  • groovy 目录中的类,一定要改动成 .groovy 后缀,IDE 才会正常识别。

  • resources/META-INF/gradle-plugins 这个目录结构是强制要求的,否则不能识别成插件。

com.asgradle.apkdist.properties 文件

假设写过 Java 的同学会知道,这是一个 Java 的 properties 文件,是 key=value 的格式。

这个文件内容例如以下:

implementation-class=com.asgradle.plugin.ApkDistPlugin

按其语义判断,是指定这个插件的入口类。

  • 英文敏感的同学可能会问了,为什么这个文件的承载目录是叫做 gradle-plugins,使用复数?没错,这里能够指定多个 properties 文件,定义多个插件,扩展性一流。能够參考 linkedin 的插件的组织方式。
  • 使用这个插件的时候。将会是这样:

    apply plugin:'com.asgradle.apkdist'

    因此。com.asgradle.apkdist 这个字符串在这里,又称为这个插件的 id。不同意跟别的插件反复,取你拥有的域名的反向就不会错。

将 plugin module 传到本地 maven 仓库

參考上一篇:拥抱 Android Studio 之四:Maven 仓库使用与私有仓库搭建。和相应的 demo 项目,将包传到本地仓库中进行測试。

加入 gradle.properties

PROJ_NAME=gradleplugin PROJ_ARTIFACTID=gradleplugin PROJ_POM_NAME=Local RepositoryLOCAL_REPO_URL=file:///Users/changbinhe/Documents/Android/repo/PROJ_GROUP=com.as-gradle.demoPROJ_VERSION=1.0.0 PROJ_VERSION_CODE=1PROJ_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 studioPROJ_LICENCE_NAME=The Apache Software License, Version 2.0 PROJ_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt PROJ_LICENCE_DEST=repoDEVELOPER_ID=your-dev-id DEVELOPER_NAME=your-dev-name DEVELOPER_EMAIL=your-email@your-mailbox.com

在 build.gradle 加入上传功能

apply plugin: 'maven'uploadArchives {repositories.mavenDeployer {repository(url: LOCAL_REPO_URL)pom.groupId = PROJ_GROUPpom.artifactId = PROJ_ARTIFACTIDpom.version = PROJ_VERSION} }

上传能够通过执行:

$ ./gradlew -p plugin/ clean build uploadArchives

在 app module 中使用插件

在项目的 buildscript 加入插件作为 classpath

buildscript {repositories {maven{url 'file:///Users/your-user-name/Documents/Android/repo/'}jcenter()}dependencies {classpath 'com.android.tools.build:gradle:2.1.0-alpha3' classpath 'com.as-gradle.demo:gradleplugin:1.0.0'} }

在 app module 中使用插件:

apply plugin: 'com.asgradle.apkdist'

命令行执行:

$ ./gradlew -p app apkdist :app:apkdist hello, world! hello, wow! your-distribution-directory

可能会遇到问题

Error:(46, 0) Cause: com/asgradle/plugin/ApkDistPlugin : Unsupported major.minor version 52.0 <a href="openFile:/Users/your-user-name/Documents/git/opensource/embrace-android-studio-demo/s5-GradlePlugin/app/build.gradle">Open File</a>

应该是本机的 JDK 版本号是1.8。默认将 plugin module 的 groovy 源代码编译成了1.8版本号的 class 文件。放在 Android 项目中,无法兼容。须要对 plugin module 的 build.gradle 文件加入两个參数:

sourceCompatibility = 1.6 targetCompatibility = 1.6

真正的实现插件需求

读者可能会观察到,到眼下为止。插件仅仅是跑通了流程,并没有实现本文提出的两个需求,

那接下来就具体实现一下。

class ApkDistPlugin implements Plugin<Project> {@Overridevoid apply(Project project) {project.extensions.create('apkdistconf', ApkDistExtension);project.afterEvaluate {//仅仅能够在 android application 或者 android lib 项目中使用if (!project.android) {throw new IllegalStateException('Must apply \'com.android.application\' or \'com.android.library\' first!')}//配置不能为空if (project.apkdistconf.nameMap == null || project.apkdistconf.destDir == null) {project.logger.info('Apkdist conf should be set!')return}Closure nameMap = project['apkdistconf'].nameMapString destDir = project['apkdistconf'].destDir//枚举每个 build variantproject.android.applicationVariants.all { variant ->variant.outputs.each { output ->File file = output.outputFileoutput.outputFile = new File(destDir, nameMap(file.getName()))}}}} }

必须指出。本文插件实现的需求。事实上能够直接在 app module 的 build.gradle 中写脚本就能够实现。这里做成插件,仅仅是为了做示范。

上传到 bintray 的过程,就不再赘述了,能够參考拥抱 Android Studio 之四:Maven 仓库使用与私有仓库搭建。

后记

至此。这系列开篇的时候挖下的坑。最终填完了。

非常多人借助这系列的解说,真正理解了 Android Studio 和它背后的 Gradle、Groovy。笔者十分高兴。

笔者也得到了非常多读者的鼓舞和支持。心中十分感激。

写博客真的是一个非常讲究执行力和耐力的事情,但既然挖下了坑,就得填上,对吧?

这半年来,个人在 Android 和 Java 平台上也做了很多其它的事情,也有了很多其它的体会。

AS 系列,打算扩充几个主题:

  • Proguard 混淆
  • Java & Android Testing
  • Maven 私有仓库深入
  • 持续集成
  • ……待发掘

记得有人说,仅仅懂 Android 不懂 Java,是非常可怕的。在这半年以来。笔者在工作中使用 Java 实现了一些后端服务,也认真学习了 JVM 字节码相关的知识并把它使用到了工作中。在这个过程中,真的非常为 Java 平台的活力、丰富的库资源、差点儿无止境的可能性所折服。接下来,会写一些跟有关的学习体会,比如:

  • Java 多线程与锁
  • JVM 部分原理
  • 字节码操作
  • Java 8部分特性
  • ……待学习

随着笔者工作的进展,我也有机会学习使用了别的语言,比如 Node.js。并实现了一些后端服务。这个语言的活力非常强。一些比 Java 现代的地方,非常吸引人。

有精力会写一写。

由于业务所需。笔者所经历的系统,正在处于像面向服务的演化过程中,我们期望建立统一的通讯平台和规范,抽象系统的资源,拆分业务。容器化。这是一个非常有趣的过程,也是对我们的挑战。

笔者也希望有机会与读者分享。

一不小心又挖下了好多明坑和无数暗坑,仅仅是为了激励自己不断往前。

在探索事物本质的旅途中。必定十分艰险,又十分有趣,沿途一定风光绚丽,让我们共勉。

參考文献

官方文档

系列导读

本文是笔者《拥抱 Android Studio》系列第四篇。其它篇请点击:

拥抱 Android Studio 之中的一个:从 ADT 到 Android Studio

拥抱 Android Studio 之二:Android Studio 与 Gradle 深入

拥抱 Android Studio 之三:溯源,Groovy 与 Gradle 基础

拥抱 Android Studio 之四:Maven 公共仓库使用与私有仓库搭建

拥抱 Android Studio 之五:Gradle 插件使用与开发

有问题?在文章下留言或者加 qq 群:453503476,希望能帮到你。

番外

笔者 kvh 在开发和运营 bugtags.com,这是一款移动时代首选的 bug 管理系统,能够极大的提升 app 开发人员的測试效率,欢迎使用、转发推荐。

笔者眼下关注点在于移动 SDK 研发。后端服务设计和实现。

我们团队长期求 PHP 后端研发,有兴趣请加以下公众号勾搭:

转载于:https://www.cnblogs.com/llguanli/p/8410021.html

总结

以上是生活随笔为你收集整理的拥抱 Android Studio 之五:Gradle 插件开发的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。