前面我们已经详细讲解了 Gradle 的 Task、Project 等基本用法,现在我们还要学习一个很重要的概念 Extension,它在 Gradle 中几乎随处可见,特别是在 Android 打包配置中。

1. 什么是Extension

我们先来看一段 Android 应用的 Gradle 配置代码:

android {compileSdkVersion 26defaultConfig {applicationId "com.hm.iou.thinapk.demo"minSdkVersion 19targetSdkVersion 26versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}
}

相信做 Android 应用开发的同学,对这段代码都快看吐了吧。记得当初刚从 eclipse 转到 Android Studio 的时候,看这些配置就像看天书一样,只知道按规定配置就可以了。但是为什么要这样配置?除此外还支持哪些配置?为什么一定要在 android 这个命名空间下配置呢?可以不可以定义自己的特殊配置呢?

上面这个 android 打包配置,就是 Gradle 的 Extension,翻译成中文意思就叫扩展。它的作用就是通过实现自定义的 Extension,可以在 Gradle 脚本中增加类似 android 这样命名空间的配置,Gradle 可以识别这种配置,并读取里面的配置内容。

2. 怎么定义Extension

2.1 ExtensionContainer

一般我们通过 ExtensionContainer 来创建 Extension,这个类与 TaskContainer 命名有点类似,TaskContainer 是用来创建并管理 Task 的,而 ExtensionContainer 则是用来创建并管理 Extension 的。通过 Project 的以下 API 可以获取到 ExtensionContainer 对象:

ExtensionContainer getExtensions​()

2.2 简单的Extension

//先定义一个普通的java类,包含2个属性
class Foo {int ageString usernameString toString() {return "name = ${username}, age = ${age}"}
}
//创建一个名为 foo 的Extension
getExtensions().create("foo", Foo)//配置Extension
foo {age = 30username = "hjy"
}task testExt << {//能直接通过 project 获取到自定义的 Extensionprintln project.foo
}

上面这个例子中,foo 就是我们自定义的 Extension 了,它里面能配置的属性与类 Foo 中的字段是一致的,在 build.gradle 中可以直接通过 project.foo 来访问。每个 Extension 实际上与某个类是相关联的,在 build.gradle 中通过 DSL 来定义,Gradle 会识别解析并生成一个对象实例,通过该类可以获取我们所配置的信息。

之前有讲过 Project 有个扩展属性是通过 ext 命名空间配置的,可以看到 ext 与这里是类似的,不同的是 ext 可以配置任何键值对的属性值,而这里只能识别我们定义的 Java 类里的属性值。

2.3 ExtensionContainer主要API功能及用法

2.3.1 创建Extension

<T> T create​(String name, Class<T> type, Object... constructionArguments)
<T> T create​(Class<T> publicType, String name, Class<? extends T> instanceType, Object... constructionArguments)

先来看看后面这个 API 所有参数的含义。

  • publicType:创建的 Extension 实例暴露出来的类类型;
  • name:要创建的Extension的名字,可以是任意符合命名规则的字符串,不能与已有的重复,否则会抛异常;
  • instanceType:该Extension的类类型;
  • constructionArguments:类的构造函数参数值

官方文档里还说明了一个特性,创建的 Extension 对象都默认实现了 ExtensionAware 接口,

The new instance will have been dynamically made ExtensionAware, which means that you can cast it to ExtensionAware.

我们来看一个具体的实例,包含了上面2个 API 的使用:

//父类
class Animal {String usernameint legsAnimal(String name) {username = name}void setLegs(int c) {legs = c}String toString() {return "This animal is $username, it has ${legs} legs."}
}//子类
class Pig extends Animal {int ageString ownerPig(int age, String owner) {super("Pig")this.age = agethis.owner = owner}String toString() {return super.toString() + " Its age is $age, its owner is $owner."}}//创建的Extension是 Animal 类型
Animal aAnimal = getExtensions().create(Animal, "animal", Pig, 3, "hjy")
//创建的Extension是 Pig 类型
Pig aPig = getExtensions().create("pig", Pig, 5, "kobe")animal {legs = 4    //配置属性
}pig {setLegs 2   //这个是方法调用,也就是 setLegs(2)
}task testExt << {println aAnimalprintln aPig//验证 aPig 对象是 ExtensionAware 类型的println "aPig is a instance of ExtensionAware : ${aPig instanceof ExtensionAware}"
}

运行 testExt 这个任务,查看结果如下:

This animal is Pig, it has 4 legs. Its age is 3, its owner is hjy.
This animal is Pig, it has 2 legs. Its age is 5, its owner is kobe.
aPig is a instance of ExtensionAware : true

2.3.2 增加Extension

前面的 create() 方法会创建并返回一个 Extension 对象,与之相似的还有一个 add() 方法,唯一的差别是它并不会返回一个 Extension 对象。

void add​(Class<T> publicType, String name, T extension)
void add​(String name, T extension)

基于前面的这个实例,我们可以换一种写法如下:

getExtensions().add(Pig, "mypig", new Pig(5, "kobe"))
mypig {username = "MyPig"legs = 4age = 1
}
task testExt << {def aPig = project.getExtensions().getByName("mypig")println aPig
}

2.3.3 查找Extension

Object findByName(String name)
<T> T findByType(Class<T> type)
Object getByName(String name)       //找不到会抛异常
<T> T getByType(Class<T> type)  //找不到会抛异常

这几个 API 很好理解,一个是通过名字去查找,一个是通过类类型去查找。

2.4 嵌套Extension

类似下面这样的配置应该随处可见:

outer {outerName "outer"msg "this is a outer message."inner {innerName "inner"msg "This is a inner message."}}

形式上就是外面的 Extension 里面定义了另一个 Extension,这种叫做 nested Extension,也就是嵌套的 Extension。本文开头的 Android 打包配置,就是采用的这种方式。

那怎么创建上面这种 Extension 呢?

class OuterExt {String outerNameString msgInnerExt innerExt = new InnerExt()void outerName(String name) {outerName = name}void msg(String msg) {this.msg = msg}//创建内部Extension,名称为方法名 innervoid inner(Action<InnerExt> action) {action.execute(inner)}//创建内部Extension,名称为方法名 innervoid inner(Closure c) {org.gradle.util.ConfigureUtil.configure(c, innerExt) }String toString() {return "OuterExt[ name = ${outerName}, msg = ${msg}] " + innerExt}}class InnerExt {String innerNameString msgvoid innerName(String name) {innerName = name}void msg(String msg) {this.msg = msg}String toString() {return "InnerExt[ name = ${innerName}, msg = ${msg}]"}}def outExt = getExtensions().create("outer", OuterExt)outer {outerName "outer"msg "this is a outer message."inner {innerName "inner"msg "This is a inner message."}}task testExt << {println outExt
}

运行结果如下:

OuterExt[ name = outer, msg = this is a outer message.] InnerExt[ name = inner, msg = This is a inner message.]

这里的关键点在于下面这2个方法的定义,只需要定义任意一个即可:

void inner(Action<InnerExt> action)
void inner(Closure c)

定义在 outer 内部的 inner ,Gradle 解析时实质上会进行方法调用,也就是会执行 outer.inner(…) 方法,而该方法的参数是一个闭包(俗称 Script Block),所以在类 OuterExt 中必须定义 inner(…) 方法。

此外,前面说到创建的 Extension 对象都是实现了 ExtensionAware 接口的,ExtensionAware 接口很简单,只包含一个方法:

ExtensionContainer getExtensions​()

所以还有一种方式来创建嵌套的 Extension,只不过这种方式没法自动赋值到 OuterExt 类里的 innerExt 对象:

def innerExt = outExt.getExtensions().create("inner", InnerExt)

3. Android的Extension

先看个 Android 的常规配置,以下是我的项目配置,截图如下所示:

我们重点看看 defaultConfig、productFlavors、signingConfigs、buildTypes 这4个内部 Extension对象是怎么定义的,通过查看源码可以找到一个叫 BaseExtension 的类,里面的相关代码如下:

private final DefaultConfig defaultConfig;
private final NamedDomainObjectContainer<ProductFlavor> productFlavors;
private final NamedDomainObjectContainer<BuildType> buildTypes;
private final NamedDomainObjectContainer<SigningConfig> signingConfigs;public void defaultConfig(Action<DefaultConfig> action) {this.checkWritability();action.execute(this.defaultConfig);
}public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {this.checkWritability();action.execute(this.buildTypes);
}public void productFlavors(Action<? super NamedDomainObjectContainer<ProductFlavor>> action) {this.checkWritability();action.execute(this.productFlavors);
}public void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {this.checkWritability();action.execute(this.signingConfigs);
}

这与前面介绍的嵌套 Extension 的定义是一致的,这里名为 android 的 Extension 是通过插件来创建的,关于插件的创建在我另一篇文章也有介绍:怎么创建Gradle插件。

在 app 的 build.gradle 里我们通常会采用插件 apply plugin: ‘com.android.application’ ,而在 library module 中则采用插件 apply plugin: ‘com.android.library’,先来看一张截图:

图中类 AppPlugin 就是插件 com.android.application 的实现类,LibraryPlugin 则是插件 com.android.library 的实现类,接着再看看 AppPlugin 里是怎样创建 Extension 的:

public class AppPlugin extends BasePlugin implements Plugin<Project> {@Injectpublic AppPlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {super(instantiator, registry);}protected BaseExtension createExtension(Project project, ProjectOptions projectOptions, Instantiator instantiator, AndroidBuilder androidBuilder, SdkHandler sdkHandler, NamedDomainObjectContainer<BuildType> buildTypeContainer, NamedDomainObjectContainer<ProductFlavor> productFlavorContainer, NamedDomainObjectContainer<SigningConfig> signingConfigContainer, NamedDomainObjectContainer<BaseVariantOutput> buildOutputs, ExtraModelInfo extraModelInfo) {return (BaseExtension)project.getExtensions().create("android", AppExtension.class, new Object[]{project, projectOptions, instantiator, androidBuilder, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, buildOutputs, extraModelInfo});}public void apply(Project project) {super.apply(project);}//省略...
}

在 createExtension() 方法中,可以看到创建了一个名为 android 的 Extension,该 Extension 的类型为 AppExtension,而 AppExtension 的继承结构为 AppExtension -> TestedExtension -> BaseExtension,所以它的实现逻辑大部分都是在 BaseExtension 里实现的。

LibraryExtension 的继承结构与 AppExtension 基本是一致的,有兴趣的可以自己看源码研究研究。

以后当我们不知道 android 里有哪些配置时,除了查看 API 文档以外,还可以直接翻看 BaseExtension 源码,基本上就能清楚了。

相关文章
Android Gradle学习(一) Gradle基础入门
Android Gradle学习(二) 如何创建Task
Android Gradle学习(三) Task进阶学习
Android Gradle学习(四) Project详解
Android Gradle学习(五) Extension详解
Android Gradle学习(六) NamedDomainObjectContainer详解
Android Gradle学习(七) Gradle构建生命周期
Android Gradle学习(八) 统计Task执行时长

Android Gradle学习(五) Extension详解相关推荐

  1. Android Gradle manifestPlaceholders 占位符详解

    Android Gradle manifestPlaceholders 占位符详解 在实际项目中,AndroidManifest里十几个地方的值是需要动态的改变(生成apk文件的时候).如果每次去改也 ...

  2. Android Telephony分析(五) ---- TelephonyRegistry详解

    本文紧接着上一篇文章<Android Telephony分析(四) -- TelephonyManager详解 >的1.4小节.  从TelephonyRegistry的大部分方法中:  ...

  3. android studio gradle3.3,Android studio 3.2 升级详解及Gradle配置

    Android studio 3.2 升级详解及Gradle配置 发布时间:2018-09-29 15:36, 浏览次数:618 , 标签: Android studio Gradle 一直在使用An ...

  4. 《Android Studio应用开发实战详解》——第1章,第1.5节Android开发学习路线图

    本节书摘来自异步社区<Android Studio应用开发实战详解>一书中的第1章,第1.5节Android开发学习路线图,作者 王翠萍,更多章节内容可以访问云栖社区"异步社区& ...

  5. Android基础总结: Camera2详解之一 API学习

    Camera2的API出来有些年头了,只是赶项目多次使用,没时间好好总结,年终了,正好结合google的官方Camera2demo 和开发中使用的情况,做个详细梳理,研究总结之后,才发现Camera2 ...

  6. android OKHttp的基本使用详解

    今天,简单讲讲Android里如何使用OKHttp. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 三.android OKHttp ...

  7. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  8. 《Android Studio应用开发实战详解》——导读

    本节书摘来自异步社区<Android Studio应用开发实战详解>一书中的目录,作者 王翠萍,更多章节内容可以访问云栖社区"异步社区"公众号查看 目 录 第1章 An ...

  9. Android APP:Preference使用详解和实例(附源码)

    Android APP:Preference使用详解和实例 一.Preference 是Android app中重要的控件之一,Settings 模块大部分都是通过Preference 实现的,这里将 ...

  10. halcon例程讲解_跟我学机器视觉-HALCON学习例程中文详解-开关引脚测量

    跟我学机器视觉-HALCON学习例程中文详解-开关引脚测量 This example program demonstrates the basic usage of a measure object. ...

最新文章

  1. Ubuntu服务器版硬件认证详情
  2. Prism for WPF初探(构建简单的模块化开发框架)
  3. 代理服务器地址在哪里看_看完这篇还不了解Nginx,那我就没你办法了
  4. c++ map用法_Pandas数据处理三板斧——map、apply、applymap详解
  5. python 字符串去重且相同字符最多出现2次_Python实现计算字符串中出现次数最多的字符示例...
  6. Python编程中报过的错
  7. s3c6410 RTC driver——读取实时时间信息 LDD3 ELDD 学习笔记
  8. 北京理工大学—计算机专业课程资源
  9. 华为荣耀盒子显示服务器忙,华为荣耀盒子m321连接后死机了怎么办?教你三大解决方法...
  10. 计算机 管理 用户,一种计算机系统及管理计算机用户权限的方法_2
  11. matlab ode 实数,关于ode45中erf函数(输入必须为实数完全数的报错问题)
  12. 好用的在线PS编辑器
  13. FaceBook和Google广告API接口文档
  14. Telnet + VTY(虚拟终端Virtual Teletype Terminal)远程管理路由器和交换机
  15. 微信小程序头像上传(一)
  16. 如何读到一个文件的最后更新日期和时间
  17. 2019-8-24 小米商城商品展示界面
  18. 微商是如何推广的呢?
  19. Spec2017编译526.blender_r报错解决
  20. NASA教你攒火星探测器,连购物车都加好了

热门文章

  1. 2021 年 8 月全国程序员薪酬终于出炉了!北京以18904元位居榜首
  2. linux shc shell脚本_用shc加密shell脚本
  3. 每月的第一个工作日执行的corn表达式
  4. 普渡大学计算机科学师生比,全美最强STEM大学排行榜!你真的不考虑一下他们?...
  5. 谷歌地图解析及ArcEngine加载谷歌地图方法
  6. 【微信小程序】微信小程序接口数据加密、解密算法
  7. excel二维表转化为一维表
  8. android 汉字拼音排序,Android实现中文按拼音排序方法
  9. 如何写好一个综述,以一个综述为例
  10. 镜头相关的基本参数总结