Gradle 之扩展Extension类 (七)
- Gradle 之Groovy基本语法(一)
- Gradle 之Groovy文件操作(二)
- Gradle 之详解Project(三)
- Gradle 之详解Task(四)
- Gradle 之初识插件(五)
- Gradle 之常用配置(六)
- Gradle 之扩展Extension类 (七)
一、前言
先看下Android中默认的扩展:
android {
compileSdkVersion 23
buildToolsVersion = '23.0.3'
defaultConfig {
applicationId "com.phj.gradle"
minSdkVersion 19
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Gradle允许将“命名空间”DSL扩展添加到目标对象。这么一大坨代码是通过设置好Android扩展后,最终才能按照相应的规则写的。他通过给用户提供相对于的配置方法,在编译打包期间,通过用户相对应的配置,再做出相应的行为。
二、自定义扩展
现在我们就创建一个和android{}类似的配置,配置格式如下:
classes {
className 'className'
person {
name 'phj'
age 24
}
}
1)、创建Groovy类
因为是两层闭包,所以需要建立两个类,他们分别是Classes 和Person 类。Classes中会Person类的成员变量,如下:
class Classes {
String className
Person person = new Person()
void className(String className){
this.className = className;
}
void person(Action<Person> action){
action.execute(person)
}
@Override
String toString() {
"className = ${this.className}, ${person}"
}
}
class Person {
int age
String name
void age(int age){
this.age = age
}
void name(String name){
this.name = name
}
String toString() {
"Person:age = ${age}, name = ${name}"
}
}
Classes类和Person是怎么关联起来的:
void person(Action<Person> action){
action.execute(person)
}
或者:
void person(Closure c) {
ConfigureUtil.configure(c, person)
}
这样就可以在classes闭包中使用person。
2)、添加到ExtensionContainer
getExtensions().add("classes", Classes)
通过getExtensions()可以拿到当前Project对象的ExtensionContainer,将写好的extension添加到ExtensionContainer
ExtensionContainer类:
public interface ExtensionContainer {
void add(String name, Object extension);
<T> T create(String name, Class<T> type, Object... constructionArguments);
<T> T getByType(Class<T> type) throws UnknownDomainObjectException;
<T> T findByType(Class<T> type);
Object getByName(String name) throws UnknownDomainObjectException;
Object findByName(String name);
@Incubating
<T> void configure(Class<T> type, Action<? super T> action);
ExtraPropertiesExtension getExtraProperties();
}
ExtensionContainer类相当于一个集合,主要包含了存储和查找方法。
- add : 向ExtensionContainer添加一个extension
- create : 创建一个extension对象,并返回该扩展
- getByType :通过Type查找extension,没有找到会有异常
- findByType :通过Type查找extension,没有找到会无异常
- getByName :通过Name查找extension,没有找到会有异常
- findByName :通过Name查找extension,没有找到会无异常
3)、获取配置
通过前两步即完成了配置,现在在脚本中可以写下下面的配置了,现在的工作就是获取该配置
classes {
className 'className'
person {
name 'phj'
age 24
}
}
通过ExtensionContainer获取:
def extension = project.getExtensions().getByName("classes") as Classes
println extension.className
三、NamedDomainObjectContainer类
1)、定义
Android中的扩展和NamedDomainObjectContainer共用的,他可以创建一个自定义名字的实例。
如buildTypes中release和debug 等:
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
或者dev和free:
productFlavors{
dev{
manifestPlaceholders = ["test_key": "测试版key"]
}
free{
applicationIdSuffix ".free"
manifestPlaceholders = ["test_key": "Free版key"]
}
}
这些都是用户自己添加的名字,事先并没定义。
2)、实现
- groovy代码
class Classes {
NamedDomainObjectContainer<Person> container
public Classes(Project project) {
container = project.container(Person)
}
void person(Action<NamedDomainObjectContainer<Person>> action){
action.execute(container)
}
@Override
String toString() {
"className = ${this.className}, ${person}"
}
}
class Person {
String name
int age
public Person(String name) {
this.name = name
}
void age(int age){
this.age = age
}
String toString() {
"Person:age = ${age}, name = ${name}"
}
}
使用NamedDomainObjectContainer的方式和之前没有的本质是一样的,如之前的Classes中有Person成员变量,现在需要时NamedDomainObjectContainer<Person>,使用NamedDomainObjectContainer来包装下。
- 添加到ExtensionContainer
def ext = getExtensions().create("classes", Classes, project) as Classes
配置如下:
classes {
person {
person1 {
age 21
}
person2 {
age 22
}
person3 {
age 23
}
}
}
- 获取
ext.container.each { value->
println value
}
最终输出:
Person:age = 21, name = person1
Person:age = 22, name = person2
Person:age = 23, name = person3
获取NamedDomainObjectContainer对象
获取NamedDomainObjectContainer对象的方法是在project中,这样在构建Classes的时候就需要将当前的Project对象传入进来,这样就可能根据该对象获取Person相关的NamedDomainObjectContainer对象。
project中获取方法如下:主要是通过传入类类型,返回一个当前类型的NamedDomainObjectContainer对象。
/**
* <p>Creates a container for managing named objects of the specified type. The specified type must have a public constructor which takes the name as a String parameter.<p>
*
* <p>All objects <b>MUST</b> expose their name as a bean property named "name". The name must be constant for the life of the object.</p>
*
* @param type The type of objects for the container to contain.
* @param <T> The type of objects for the container to contain.
* @return The container.
*/
<T> NamedDomainObjectContainer<T> container(Class<T> type);
/**
* <p>Creates a container for managing named objects of the specified type. The given factory is used to create object instances.</p>
*
* <p>All objects <b>MUST</b> expose their name as a bean property named "name". The name must be constant for the life of the object.</p>
*
* @param type The type of objects for the container to contain.
* @param factory The factory to use to create object instances.
* @param <T> The type of objects for the container to contain.
* @return The container.
*/
<T> NamedDomainObjectContainer<T> container(Class<T> type, NamedDomainObjectFactory<T> factory);
/**
* <p>Creates a container for managing named objects of the specified type. The given closure is used to create object instances. The name of the instance to be created is passed as a parameter to
* the closure.</p>
*
* <p>All objects <b>MUST</b> expose their name as a bean property named "name". The name must be constant for the life of the object.</p>
*
* @param type The type of objects for the container to contain.
* @param factoryClosure The closure to use to create object instances.
* @param <T> The type of objects for the container to contain.
* @return The container.
*/
<T> NamedDomainObjectContainer<T> container(Class<T> type, Closure factoryClosure);
container(Class<T> type)
上面的container(Class<T> type);的注释中表示了,传入的type类中需要有个name的String类型的字段,以及对应的构造方法,因为该name作为了之后实体的唯一标示。
也可以在类外包装
// 1、groovy类
class P {
String name
int age
public P(String name) {
this.name = name
}
void age(int age){
this.age = age
}
String toString() {
"P:age = ${age}, name = ${name}"
}
}
// 2、采用NamedDomainObjectContainer包装
def p = container(P) as NamedDomainObjectContainer<P>
// 3、添加到扩展容器中
project.extensions.add('platform',p)
// 4、编写脚本
platform {
wandoujia {
age = 1
}
xiaomi {
age = 2
}
}
本质是一样的,不过多说明:
- 1、编写groovy类
- 2、采用NamedDomainObjectContainer包装
- 3、添加到扩展容器中
- 4、编写脚本
四、android{} 常用配置
android {
compileSdkVersion 23
buildToolsVersion = '23.0.3'
defaultConfig {
applicationId "com.phj.gradle"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dexOptions{
dexInProcess true
javaMaxHeapSize "4g"
}
productFlavors {
wandoujia {
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
versionNameSuffix 'phj'
}
}
externalNativeBuild{
ndkBuild{
path file("src\\main\\jni\\Android.mk")
}
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude ['lib/armeabi-v7a/libavcodec-57.so',
'lib/armeabi-v7a/libavdevice-57.so']
}
signingConfigs {
config {
keyAlias '...'
keyPassword '...'
storeFile file('...')
storePassword '...'
}
}
sourceSets {
main {
java.srcDirs 'src/main/java','src/main/java2'
manifest.srcFile 'src/main/AndroidManifest.xml'
resources.srcDirs = ['src/main/res']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
jniLibs.srcDirs = ['../lib']
}
}
}
android是com.android.build.gradle包下的AppExtension类,所有能设置的属性都在该类的方法中。主要在其父类BaseExtension中设置的。
BaseExtension中的属性如下:
private final List<List<Object>> transformDependencies = Lists.newArrayList();
private final AndroidBuilder androidBuilder;
private final SdkHandler sdkHandler;
private final ProductFlavor defaultConfig;
private final AaptOptions aaptOptions;
private final LintOptions lintOptions;
private final ExternalNativeBuild externalNativeBuild;
private final DexOptions dexOptions;
private final TestOptions testOptions;
private final CompileOptions compileOptions;
private final PackagingOptions packagingOptions;
private final JacocoOptions jacoco;
private final Splits splits;
private final AdbOptions adbOptions;
private final NamedDomainObjectContainer<ProductFlavor> productFlavors;
private final NamedDomainObjectContainer<BuildType> buildTypes;
private final NamedDomainObjectContainer<SigningConfig> signingConfigs;
private final List<DeviceProvider> deviceProviderList = Lists.newArrayList();
private final List<TestServer> testServerList = Lists.newArrayList();
private final List<Transform> transforms = Lists.newArrayList();
private final DataBindingOptions dataBinding;
private final NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer;
private String target;
private Revision buildToolsRevision;
private List<LibraryRequest> libraryRequests = Lists.newArrayList();
private List<String> flavorDimensionList;
private String resourcePrefix;
private ExtraModelInfo extraModelInfo;
private String defaultPublishConfig = "release";
private boolean publishNonDefault = false;
private Action<VariantFilter> variantFilter;
protected Logger logger;
private boolean isWritable = true;
protected Project project;
boolean generatePureSplits = false;
private boolean enforceUniquePackageName = true;
下面介绍下常用的配置:
- compileSdkVersion : 编译时的版本
- buildToolsVersion : 构建工具版本
- defaultConfig{} : 默认的配置
BaseExtension中的设置源码如下,能设置的配置需要看下ProductFlavor类
public void defaultConfig(Action<ProductFlavor> action) {
this.checkWritability();
action.execute(this.defaultConfig);
}
- buildTypes : 构建的类型,设置内容详见BuildType类
- productFlavors : 构建输入的变体,设置详见ProductFlavor类
- dexOptions : dex的设置,打包输入的文件格式。配置详见DexOptions类
- externalNativeBuild : jni开发配置文件,分为ndkBuild和cmake两种编译方式
- lintOptions : 检查代码工具的配置,利于排查代码问题,配置内容详见LintOptions类
- packagingOptions : 去除相同包操作配置
- signingConfigs :签名配置
- sourceSets : 设置资源的路径