内容简介:通过上一节Groovy其实和js、python、kotlin类似,弱类型、闭包等特性,又完全兼容java,学习起来较为简单。具体可以专题进行学习Groovy语法。可以说每一个build.gradle都是一个Project对应项目中就是某一个module,直接在其中定义的属性或者方法都可以使用project来调用。如
通过上一节 Android中的Gradle之配置及构建优化 ,我们已经了解了Gradle的各个配置项的含义,知道了如何优化构建配置,但只会用别人提供好的,无法按自己的意愿实现功能。通过本章节,我们将简单介绍Groovy,了解Gradle中的Project与Task,引入gradle脚本,根据android plugin插件提供的功能自定义扩展,以及写自己的Task及自己的gradle插件,相信看完之后能对gradle有进一步的了解。
二、Groovy语法简介
Groovy其实和js、 python 、kotlin类似,弱类型、闭包等特性,又完全兼容java,学习起来较为简单。具体可以专题进行学习Groovy语法。
1、与 java 比较
- Groovy完全兼容java的语法,也就是说在Groovy可以编写java代码并运行,最终编译成java字节码。
- 句末的分号是可选的
- 类、方法默认是public的
- 编译器自动添加getter/setter方法
- 属性可以使用点号获取
- 方法如果有返回值,最后一个表达式的值即作为返回,省略return。
- ==等同于equals()
- 没有NullPointerException
2、Groovy高效特性
- assert语句可以在任何位置断言
- 弱类型变量
- 调用方法如果有参数,可以省略括号
- 字符串的表示
- 集合类api,如map、list的某些方法
- 闭包
3、Groovy语法简单演示
// 1 可选的类型定义 def version = 1 // 2 assert assert version == 2 // 3 括号是可选的 println version // 4 字符串 def s1 = 'Groovy' def s2 = "version is ${version}" def s3 = '''三个 分号 可以 换行 ''' println s1 // Groovy println s2 // version is 1 println s3 // 三个 // 分号 // 可以 // 换行 // 5 集合api // list def buildTools = ['ant','maven'] buildTools << 'gradle' println buildTools.getClass() // class java.util.ArrayList assert buildTools.size() == 3 // 没有异常 // map def buildYears = ['ant':2000,'maven':2004] buildYears.gradle = 2009 println buildYears.ant // 2000 println buildYears['gradle'] // 2009 println buildYears.getClass() // class java.util.LinkedHashMap // 6 闭包 def c1 = { v -> println v } def method1(Closure closure){ closure('param') } method1(c1) // param method1{ c1 "hello" } 复制代码
三、Gradle中的基础概念
1、Project
可以说每一个build.gradle都是一个Project对应项目中就是某一个module,直接在其中定义的属性或者方法都可以使用project来调用。如 def valueTest = 5
可以使用 valueTest
或者 project.valueTest
来获取valueTest的值。
根目录的build.gradle也是一个Project,在其中定义 ext{valueTest = 5}
,在module中的build.gradle中可以直接使用 rootProject.valueTest
或者 rootProjext.ext.valueTest
来引用。这里的ext是一个全局变量的意思。
2、Task
Project又是由一个或者多个Task组成,Task存在着依赖关系,依赖关系又保证了任务的执行顺序。比如我们熟知的Task有clean,jar,assemble等。
3、Gradle构建声明周期
Gradle的声明周期分为三段:
- 初始化阶段:读取根工程中 settings.gradle 中的 include 信息,决定有哪几个工程加入构建,创建Project实例,如
include ':app',':example'
- 配置阶段:执行所有工程的 build.gradle 脚本,配置Project对象,创建、配置Task及相关信息。该阶段会执行build.gradle中的命令,如直接写在Project中的println命令
- 运行阶段:根据gradle命令传递过来的task名称,执行相关依赖任务
四、Gradle脚本的引入
引入gradle脚本,可以实现对gradle代码的复用,减少维护成本。可以通过 apply from: 'other.gradle'
来对other.gradle进行引入。日常开发中,我们可以通过如下几点优化我们的项目:
1、将项目中的配置复用项提取出来
在gradle中有很多配置项,如版本号,版本名称,最小支持SDK版本,是否使用混淆,依赖的第三方库等。这些配置很有可能在项目中复用,如需修改,可能会导致遗漏,将这些配置提取出来,供各个build.gradle使用。具体如下:
1、在根目录新建config.gradle
ext { android = [ compileSdkVersion: 27, minSdkVersion : 16, targetSdkVersion : 27, versionCode : 1, versionName : "1.0.0", multiDexEnabled : true ] version = [ kotlin_version : "1.2.50", support_version : "27.1.1", constraint_version : "1.1.3", junit_version : "4.12", runner_version : "1.0.2", espresso_core_version: "3.0.2" ] dependencies = [ "androidJUnitRunner": "android.support.test.runner.AndroidJUnitRunner", "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${version["kotlin_version"]}", "appcompat_v7" : "com.android.support:appcompat-v7:${version["support_version"]}", "constraint_layout" : "com.android.support.constraint:constraint-layout:${version["constraint_version"]}", "junit" : "junit:junit:${version["junit_version"]}", "runner" : "com.android.support.test:runner:${version["runner_version"]}", "espresso_core" : "com.android.support.test.espresso:espresso-core:${version["espresso_core_version"]}" ] } 复制代码
2、在根目录build.gradle中增加 apply from:"config.gradle"
3、修改module的gradle文件
android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] defaultConfig { applicationId "net.loosash.learngradle" minSdkVersion rootProject.ext.android["minSdkVersion"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"] } ... } ... dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation rootProject.ext.dependencies["kotlin"] implementation rootProject.ext.dependencies["appcompat_v7"] implementation rootProject.ext.dependencies["constraint_layout"] testImplementation rootProject.ext.dependencies["junit"] androidTestImplementation rootProject.ext.dependencies["runner"] androidTestImplementation rootProject.ext.dependencies["espresso_core"] } 复制代码
2、对于build.gradle中重复部分进一步提取
1、对于依赖工程或者组件化工程,建议将依赖module中的配置也提取出来。在根目录新建default.gradle文件。
apply plugin: 'com.android.library' android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] defaultConfig { minSdkVersion rootProject.ext.android["minSdkVersion"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"] } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // support implementation rootProject.ext.dependencies["appcompat_v7"] // test testImplementation rootProject.ext.dependencies["junit"] androidTestImplementation rootProject.ext.dependencies["runner"] androidTestImplementation rootProject.ext.dependencies["espresso_core"] } 复制代码
2、修改使用默认配置的module中的build.gradle文件
apply from:"../default.gradle" android{ // 写入该模块特定的配置 resourcePrefix "example_" //给 Module 内的资源名增加前缀, 避免资源名冲突 } dependencies { // 该模块使用的依赖 } 复制代码
3、对build.gradle文件前后做一个对比
修改后的好处在于,比如在工程中多处使用support包中的依赖,一次修改版本号即可对全部工程生效,降低了维护成本。
五、自定义Task任务
1、定义任务
最常用的写法,执行 ./gradlew hello
打印出 hello world
:
task hello{ println 'hello world' } 复制代码
在android studio创建项目后,会在根目录的build.gradle中创建clean任务,就是删除build文件夹下文件
task clean(type: Delete) { delete rootProject.buildDir } 复制代码
说明:Task创建的时候可以通过 type: SomeType 指定Type,Type其实就是告诉Gradle,这个新建的Task对象会从哪个基类Task派生。比如,Gradle本身提供了一些通用的Task,最常见的有Copy 任务。Copy是Gradle中的一个类。当我们:task myTask(type:Copy)的时候,创建的Task就是一个Copy Task。类似的,有如下写法:
task copyDocs(type: Copy) { from 'src/main/doc' into 'build/target/doc' } 复制代码
2、依赖关系
Gradle中Task存在着依赖关系,在执行过程中会先执行所依赖的任务,再执行目标任务。
task hello { doLast { println 'Hello world!' } } Task intro(dependsOn: hello) { doLast { println "I'm Gradle" } } 复制代码
执行结果
> Task :hello Hello world! > Task :intro I'm Gradle 复制代码
3、分组和描述
task可以增加分组和说明
task hello { group 'Custom Group 1' description 'This is the Hello Task' doLast { println 'Hello world!' } } task intro(dependsOn: hello) { group 'Custom Group 2' description 'This is the intro Task' doLast { println "I'm Gradle" } } 复制代码
输入 ./gradlew tasks
Custom Group 1 tasks -------------------- hello - This is the Hello Task Custom Group 2 tasks -------------------- intro - This is the intro Task 复制代码
4.获取已经定义的任务,并增加执行处理
在上面已有的Task hello中增加执行处理。
task hello { group 'Custom Group 1' description 'This is the Hello Task' doLast { println 'Hello world!' } } task intro(dependsOn: hello) { group 'Custom Group 2' description 'This is the intro Task' doLast { println "I'm Gradle" } } tasks.hello{ doFirst{ println "prepare to say" } } 复制代码
执行 ./gradlew intro
得到如下输出
> Task :hello prepare to say Hello world! > Task :intro I'm Gradle 复制代码
六、自定义构建功能
1、buildTypes
可以利用这里的属性,针对debug版本和release版本对包名进行修改。具体使用如下:
1)对applicationId进行修改,使其能同时安装debug版本和release版本
android { ... buildTypes { debug { // bebug版本包名为xxx.xxx.xxx.debug applicationIdSuffix ".debug" } ... } } 复制代码
2)对release版本进行签名配置
signingConfigs { release { keyAlias 'xxxx' keyPassword 'xxxxxx' storeFile file('your-keystore-path') storePassword 'xxxxxx' } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } } 复制代码
2、productFlavors
可以根据该属性进行生成不同的APK版本,版本可以携带不同的特性,最常用的就是多渠道打包、根据不同环境生成不同APK。
productFlavors{ productA{ // 定义特定版本号 // applicationIdSuffix ".a" applicationId "xxx.xxx.xxx.a" // 定义特定版本名称 versionName "version-a-1.0" // 定义特定的BuildConfig buildConfigField("String","CUSTUMER_CONFIG","xaxaxaxa") } productB{ applicationId "xxx.xxx.xxx.b" versionName "version-b-1.0" buildConfigField("String","CUSTUMER_CONFIG","xbxbxbxb") } } dependencies{ ... // 特定版本依赖 productACompile 'io.reactivex.rxjava2:rxjava:2.0.1' ... } 复制代码
3、增加自定义处理
将生成的apk以自定义命名复制到自定义文件夹
applicationVariants.all { variant -> tasks.all { if ("assemble${variant.name.capitalize()}".equalsIgnoreCase(it.name)) { it.doLast { copy { rename { String fileName -> println "------------${fileName}--------------" fileName.replace(".apk", "-${defaultConfig.versionCode}.apk") } def destPath = file("/Users/solie_h/Desktop/abc/") from variant.outputs.first().outputFile into destPath } } } } } 复制代码
七、总结
到这里,其实其实刚刚是使用Gradle的开始,我们可以操作Gradle完成一些自己的需求,但想对外提供Gradle插件还需要一些功夫,接下来还要继续对android plugin源码进行研读,也找一些有Gradle插件的开源项目进行学习,如Replugin、Tinker,进一步提高自己对Gradle的认识。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Android 自定义 View (04自定义属性)
- Vue自定义组件(简单实现一个自定义组件)
- Android 自定义View:深入理解自定义属性(七)
- Qt编写自定义控件20-自定义饼图 原 荐
- SpringBoot2 | SpringBoot自定义AutoConfiguration | SpringBoot自定义starter(五)
- 『互联网架构』软件架构-springboot自定义视图和自定义Starter(90)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
New Dark Age
James Bridle / Verso Books / 2018-7-17 / GBP 16.99
As the world around us increases in technological complexity, our understanding of it diminishes. Underlying this trend is a single idea: the belief that our existence is understandable through comput......一起来看看 《New Dark Age》 这本书的介绍吧!