这个 build.Gradle 文件来自 drakeet 大神的 Meizi 项目
我直接在代码上加注释,参照着注释看代码就行,是否是发现有不少代码平时都没看见过。html
1 //Model都有各自的build.gradle,这里声明该Model做为主项目,常见的还有另外一个取值: 2 //apply plugin: 'com.android.library' 声明该Model做为库使用,固然还有其余取值,后面博客会介绍 3 apply plugin: 'com.android.application' 4 5 //这里是在as里引入一个retrolambda插件,具体我也不大懂,能够看看这篇博客: 6 //http://blog.csdn.net/zhupumao/article/details/51934317?locationNum=12 7 apply plugin: 'me.tatarka.retrolambda' 8 9 //这里是groovy的代码了,定义了一个获取时间的方法,groovy是兼容java,它能够直接使用jdk里的方法 10 def releaseTime() { 11 return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) 12 } 13 14 //file()是Project.java里的一个方法,这里定义一个File类型的对象,Project后面博客会介绍到 15 def keyStore = file('meizhi.keystore') 16 17 android { 18 19 //这个你们应该很熟悉了,有疑问的应该是后面的代码,这里表示获取一些全局变量 20 //这些变量的值在根目录下的build.gradle中定义,具体能够看看这篇博客: 21 //http://blog.csdn.net/fwt336/article/details/54613419 22 compileSdkVersion rootProject.ext.android.compileSdkVersion 23 buildToolsVersion rootProject.ext.android.buildToolsVersion 24 25 //同理,这里都是经过获取全局设置的变量值来进行相关配置,这样作的好处在于当 26 //你的项目里有多个model时,能够方便修改这些公共的配置,只须要修改一个地方就能够同步了 27 defaultConfig { 28 applicationId rootProject.ext.android.applicationId 29 minSdkVersion rootProject.ext.android.minSdkVersion 30 targetSdkVersion rootProject.ext.android.targetSdkVersion 31 versionCode rootProject.ext.android.versionCode 32 versionName rootProject.ext.android.versionName 33 } 34 35 //这里应该是设置打包后的apk里的META-INF移除指定的文件吧 36 packagingOptions { 37 exclude 'META-INF/DEPENDENCIES.txt' 38 //省略部分exclude 代码... 39 } 40 41 //关闭指定的lint检查 42 lintOptions { 43 disable 'MissingTranslation', 'ExtraTranslation' 44 } 45 46 //lint检查到错误时不中断编译,好像是说lint检查是为优化代码,发现的错误其实并不会致使程序异常 47 //因此有的时候及时发现Lint检查错误仍是能够直接运行查看效果 48 lintOptions { 49 abortOnError false 50 } 51 52 //签名的相关配置 53 signingConfigs { 54 //这个标签名能够随意命名,这里的做用大概相似于定义一个对象,该对象里设置好了签名须要的各类配置 55 //能够定义不止一种配置的签名对象,例如常见的还有 debug{...}, release{...},而后在buildTypes{}里 56 //经过 signingConfigs.app1 进行调用 57 app1 { 58 //签名的相关配置,网上资料不少,STOREPASS, KEYALIAS, KEYPASS 这些常量是定义在 59 //gradle.properties 文件里,若是没有该文件手动建立便可,这样能够保证安全 60 //只有定义在 gradle.properties 里的常量才能够直接经过常量名引用 61 storeFile file('meizhi.keystore') 62 storePassword project.hasProperty('STOREPASS') ? STOREPASS : '' 63 keyAlias project.hasProperty('KEYALIAS') ? KEYALIAS : '' 64 keyPassword project.hasProperty('KEYPASS') ? KEYPASS : '' 65 } 66 } 67 68 //编译,打包的项目配置 69 buildTypes { 70 71 debug { 72 //在 BuildConfig 里自定义一个 boolean 类型的常量 73 //更多资料能够查看:http://stormzhang.com/android/2015/01/25/gradle-build-field/ 74 buildConfigField "boolean", "LOG_DEBUG", "true" 75 76 debuggable true 77 applicationIdSuffix ".debug" 78 } 79 80 release { 81 buildConfigField "boolean", "LOG_DEBUG", "false" 82 83 debuggable false 84 85 //开启混淆 86 minifyEnabled true 87 //删除无用的资源 88 shrinkResources true 89 //混淆文件 90 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 91 if (keyStore.exists()) { 92 println "Meizhi: using drakeet's key" 93 //根据在signingConfigs.app1里的签名配置进行签名 94 signingConfig signingConfigs.app1 95 } else { 96 println "Meizhi: using default key" 97 } 98 99 //这段代码应该会在大神的项目里挺常见的,我在不少项目里都看见过了 100 //这也是groovy的代码,这里的代码做用是重命名最后打包出来的apk 101 //根据 def fileName 设置的格式来命名,${}表示的是某个变量的引用 102 //例如根据设置的格式最后apk命名多是: Meizhi_v1.0.0_2017-03-28_fir.apk 103 //至于 applicationVariants 这些变量含义后面博客会介绍 104 applicationVariants.all { variant -> 105 variant.outputs.each { output -> 106 def outputFile = output.outputFile 107 if (outputFile != null && outputFile.name.endsWith('.apk')) { 108 def fileName = "Meizhi_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk" 109 output.outputFile = new File(outputFile.parent, fileName) 110 } 111 } 112 } 113 } 114 115 //这里的做用跟 singingConfigs 差很少,只是为不一样的 flavor 设置一些属性 116 //常见的设置好比设置不一样的渠道编号,设置不一样的 api 服务器等等 117 productFlavors { 118 fir { 119 //这个的做用是将 AndroidManifest.xml 里的占位符 ¥{UMENG_CHANNEL_VALUE} 的值替换成fir 120 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "fir"] 121 } 122 GooglePlay { 123 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "GooglePlay"] 124 } 125 Umeng { 126 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Umeng"] 127 } 128 } 129 } 130 131 //设置JDK的版本经过compileOptions 132 compileOptions { 133 sourceCompatibility JavaVersion.VERSION_1_8 134 targetCompatibility JavaVersion.VERSION_1_8 135 } 136 137 //lint的相关配置吧 138 lintOptions { 139 disable "InvalidPackage" 140 lintConfig file("lint.xml") 141 } 142 } 143 144 //这里就不用说了 145 dependencies { 146 compile fileTree(dir: 'libs', include: ['*.jar']) 147 compile project(":libraries:headsupcompat") 148 compile project(":libraries:smooth-app-bar-layout") 149 //as默认会去下载传递依赖,下面是指定不须要去下载传递依赖 150 compile ('com.squareup.retrofit2:retrofit:2.1.0') { 151 exclude module: 'okhttp' 152 } 153 retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0' 154 //省略部分compile代码... 155 }
1.apply plugin: 'com.android.application' 据说这是调用一个方法?java
2.rootProject.ext.android.compileSdkVersion, 不用 ext 来设置全局变量是否能够?android
3.defaultConfig{}, packagingOptions{}, signingConfigs{}, buildTypes{} 等等这些,我怎么知道 Android{} 里都有哪些可使用?git
...github
·徐宜生写的《Android群英传:神兵利器》第4章:与Gradle的爱恨情仇
·retrolambda使用教程
·Gradle配置全局变量
·GRADLE自定义你的BUILDCONFIGswift
Groovy 是一种脚本语言,既然是脚本语言,那么它也就有脚本语言的那些特色:使用动态类型、末尾不用分号等等。另外,它又是基于 Java 上设计的语言,也就是 Groovy 兼容 Java,可使用 JDK 里的各类方法,你能够在 Groovy 文件里写 Java 代码里,照样能够正常编译运行。api
关于语法的详细的介绍在末尾有连接,这里就只是挑出我认为比较重要的,并且跟 java 有区别的,在阅读代码时可能会看不懂的一些语法进行记录。数组
这方面在 build.gradle 文件里也不怎么常见到使用,由于 groovy 是动态类型,定义任何类型均可以只使用 def 来定义,因此若是使用具体的好比 char, int 等类型时须要强制转换吧。有须要的能够本身查阅末尾的参考连接。安全
java 只支持用 "..."
双引号来表示字符串服务器
groovy 支持使用 '...'
, "..."
, '''...'''
, """..."""
, /.../
, $/.../$
即单引号,双引号等6种方法来表示字符串
至于各类表示方法有什么区别,具体能够参考末尾的连接,这里简单提提,'...'
, "..."
只支持单行字符串,不支持多行,剩下的四种都支持多行字符串,以下图
斜杠我也不多见,常见的是带有 ${}
的字符串,好比: println "blog's url: ${blogUrl}"
这是 groovy 的 GString 特性,支持字符串插值,有点了相似于变量引用的概念,但注意,在 '...'
, '''...'''
单引号表示的字符串里不支持 ${}
。固然,若是你要使用 java 的方式,用 +
来拼接也能够。
定义和初始化
定义很简单,List 的话使用 []
定义,各项用 ,
隔开便可。Map 的话使用 [:]
,各项也是用 ,
隔开,如:
有一点跟 java 不一样的是, groovy 集合里不要求每一项都是同类型,好比能够这样定义 def list = [1, 'dasu', true]
,集合里包含数字,字符串,布尔值三种类型。
使用
经过下标操做符 []
读写元素值,并使用正索引值访问列表元素或负索引值从列表尾部访问元素,也可使用范围,或使用左移 <<
追加列表元素,如
跟 java 不一样的是, groovy 并不存在下标访问越界,当下标为负数时则从右开始算起,当指定的下标没有存放值时返回 null。
groovy 其实没有严格区分数组和集合,数组的定义和使用方法跟集合同样,只是你须要强制声明为数组,不然默认为集合,如
上面的初始化方式是否是跟 java 不同,这一点须要注意下,java 是用 {}
来初始化,但在 groovy 里面, {}
表示的是闭包,因此这点须要注意一下。
上面的是 groovy 与 java 不一样的一些基本语法,下面介绍一些我本身认为是 groovy 比较重要的特性,若是要看懂 build.gradle 里的代码,明白下面介绍的会比较有帮助。
方法的括号能够省略
groovy 定义方法时能够不声明返回类型和参数类型,也能够不须要 return 语句,最后一行代码默认就是返回值。
而在调用方法时能够将括号省略,不省略的时候以下
上面的方式不陌生吧,再来看看下面的代码
上面就是调用方法时省略掉圆括号的写法,再来看一种状况
此次定义一个参数为 map 类型的方法,若是咱们在调用方法的时候才对参数进行定义和初始化会是什么样的呢?以下
以前说过了,groovy 调用方法时能够将括号省略掉,这样一来再看下
这样子的格式是否是看着以为很眼熟,没错,就是 build.gradle 里的第一行代码。
若是有看过个人上一篇 build.gradle 博客的话,如今对疑问1是否是就有些理解了呢。
上图那代码若是把省略的括号补上的话,你们应该就会熟悉点了
调用了 apply() 方法,该方法传入一个 map 参数,咱们来看看是否是这样,用as查看下源码,以下
没错吧,apply() 实际上是个方法,参数为 map 类型,并且 key 的取值也给你规定了 from
, plugin
, to
三种,是否是确实在别人的 build.gradle 代码里也有看见过相似 apply from ***
,这样一来就明白多了吧。
好了,而后你再从新去看一下 build.gradle 里的代码,是否是对每一行的代码都有了新的见解了。
其实 build.gradle 里的每一行代码都是在调用一个方法,好比下面这些咱们常见的:
每一行都是在调用一个方法,前面是方法名,后面是方法的参数,只是把括号省略掉了而已,感兴趣的你能够再本身用as点进去看看源码是否是这样。
方法最后一个参数是闭包能够提取出来接到后面
闭包是 groovy 的一大特性,我理解也不深,也讲不大清楚,感兴趣的可自行网上查阅学习,简单的说就是一个用 {..}
包起来的代码块,好比 build.gradle 里的 defaultConfig{...}
, buildTypes{...}
, dependencies{...}
等等这些大括号包起来的代码块就是闭包,闭包代码块最后一句代码做为闭包的返回值。
当闭包做为方法的最后一个参数,能够将闭包从参数圆括号中提取出来接在最后,若是闭包是惟一的一个参数,则方法参数所在的圆括号也能够省略。对于有多个闭包参数的,只要是在参数声明最后的,都可以按上述方式省略,举个例子。
上面定义一个 add 方法,最后一个参数为闭包,调用的时候传入一个闭包,闭包的最后一行代码 1+1
做为闭包返回值返回,闭包返回值做为方法的第二个参数传入方法中计算加法,因此最终输出3。上面的调用也能够写成下面的方式:
注意,这是调用 add() 方法,而不是在定义,1 是第一个参数,括号后的闭包 { 1+2 }
是方法的第二个参数,这就是 groovy 的特性,闭包能够提取出来。那么再想一想,若是方法只有一个闭包参数,再结合 groovy 能够省略掉括号的特性,这样子调用一个方法将会是什么样子呢?
是否是又感受很熟悉,对吧,就是 build.gradle 里的 defaultConfig{...}
, buildTypes{...}
, dependencies{...}
等等这些。
因此,结合上面讲的两点:能够省略方法括号和闭包能够提取接到括号后面,这样一来, build.gradle 里的代码其实就是在调用各类方法,defaultConfig
是一个方法,compileSdkVersion
也是一个方法。 build.gradle 里的每一行代码前面是方法名,后面则是方法须要的参数,参数有的是基本类型,有的则是闭包类型。
集合遍历 each/all
就先把上一篇博客里的在一段在 build.gradle 里很常见的代码贴出来
这段代码做用就是对打包生成的 apk 按照规定的格式进行重命名,在不少大神的 build.gradle 里都会碰见过,其实这一段代码就是 groovy 代码,all
和 each
是集合的一种操做,all
后面跟着的是一个参数为 variant
的闭包,表示对 applicationVariants
集合里全部的对象都运行后面的闭包,同理 each
后面也是跟着一个参数为 output
的闭包,相似于 java 里的 for 循环操做。因此这里要理解的应该是 applicationVariants
表明的是什么,这点我也还不是很懂,后面若是搞懂了的话会在以后的博客里介绍出来。
另外,我还有个疑问来着, all
操做和 each
操做有什么区别么,感受都是对集合里全部的元素进行操做,若是有懂的可以告知就太感谢了,查了挺多资料貌似还不是很明白。
官方文档
Groovy语言规范-语法(官方文档翻译)
Groovy操纵集合秘籍
咱们来举个例子,就像系列一的博客里介绍的 build.gradle 里有这样一段代码:
咦!这代码是第一次在 build.gradle 里看见过,是什么意思呢?不怕,我有绝招:
Android Studio大法---看源码,还有什么是不能经过看源码注释解决的么
是跳到源码了,但是为何没有方法的注释说明呢,这方法是什么鬼谁知道啊。再仔细看看 as 的提示,原来打开的是个 class 文件啊。
as 的提示那里应该会有个下载和选择源码位置的按钮的啊,搞不懂为何不出现。
另外,咱们知道,as 通常会默认先打开 xxx-sources.jar 也就是 xxx 的 java 文件源码,若是没有源码文件,才会打开 xxx.jar 的 class 代码。咱们看一下,打开的是什么文件。
没错,as 打开的是 gradle-2.3.0.jar,说明 as 没有找到 gradle-2.3.0-sources.jar 源码文件,咱们看一下究竟是不是这样
as 标题栏会显示你当前打开的文件的具体位置,好了,知道了 gradle-2.3.0.jar 在电脑里的位置了,咱们到那个目录下看看
只有一个 gradle-2.3.0.jar 文件和一个 pom 文件,正常的话应该还要有个 xxx-sources.jar 文件才对,就像下面这样
由于这里没有 gradle-2.3.0-sources.jar 文件,因此 as 没办法打开带有方法注释的源码文件了。既然知道问题,那么就好解决了,as 的提示条也没有下载的按钮,那咱们就本身去下载好了
打开 maven 网站,在这里能够下载 gradle 插件。在搜索框中输入 com.android.tools.build
,为何输入这个,你打开 project 下的 build.gradle 文件看看就知道了
这些就是 gradle 插件,咱们在根目录下的 build.gradle 配置的 gradle 版本其实就是来这里下载的,应该是吧。
下载后获得的就是这些文件了,接下去就是找到相应文件的位置,复制一份过去
打开相应的插件文件夹,选择相应的版本,最后将下载的 xxx-sources.jar 复制一份进去,最后的样子以下
好了,大功告成,咱们再打开 as,记得 ReBuild 一下,而后再试试查看 build.gradle 源码会是什么样子
翻译一下英文大概就是说, lintOptions.abortOnError = false 是设置即便 lint 检查时发现错误也不中止构建程序的运行。
这种方法比去官方的 api 文档里查阅方便多了吧。再来看看几个效果。
这里就能够看到源码里介绍 Plugin 的值都有哪些,分别对应哪一个类,该去哪一个类看它的做用是什么,干什么的。
借助 as,咱们甚至能够很容易的查到 android{...}
这个括号里能使用的方法都有哪些,若是要看各自的做用是干什么的,再继续点进去查看注释就好了。是否是发现,咱们在 build.gradle 里的 android{...}
使用过的标签名原来都在这里的啊。