内容简介:通过上一节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)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Design Patterns
Elisabeth Freeman、Eric Freeman、Bert Bates、Kathy Sierra、Elisabeth Robson / O'Reilly Media / 2004-11-1 / USD 49.99
You're not alone. At any given moment, somewhere in the world someone struggles with the same software design problems you have. You know you don't want to reinvent the wheel (or worse, a flat tire),......一起来看看 《Head First Design Patterns》 这本书的介绍吧!