文章目录
Gradle基础
Gradle环境配置
下载gradle解压后找到目录,例如:D:\android\gradle\gradle-4.4\bin。把地址配置到环境变量path中
Android Gradle构建生命周期
- Initialization:初始化阶段,会执行项目根目录下的settings.gradle文件,来分析哪些项目参与构建
- Configuration:配置阶段,配置阶段会去加载所有参与构建的项目的build.gradle文件,
会将每build.gradle文件实例化为一个Gradle的project对象 - Execution:执行阶段,开始打包
Gradle Wrapper
- Gradle的一层包装,便于开发中统一构建版本。当启动Gradle时,Wrapper会检查Gradle有没有被下载关联。 在 Wrapper中包含gradle-wrapper.jar和gradle-wrapper.properties。
- gradlew和gradlew.bat分别是Linux和Windows下的可执行脚本。Gradle-wrapper.jar是具体业务逻辑包,gradlwe最终还是使用Java执行的这个jar。
- gradle-wrapper.properties中的配置:distributionBase 下载的gradle压缩包解压后存储主路径;distributionPath 相对于distributionBase的解压后的gradle压缩包的路径; zipStoreBase 同distributionBase ,只不过是存放zip压缩包的; zipStorePath 同distributionPath ,只不过是存放zip压缩包的; distributionUrl Gradle发行版本压缩包的下载地址。
Groovy基础
Groovy是基于JVM虚拟机的一种动态语言, 它的语法和Java非常相似,是一种灵活的动态脚本语言。每个Gradle的build脚本都是一个Groovy脚本。Groovy是弱类型语言,语法更加灵活,不用像Java中的类以及数据类型需要强制转换,如下示例:
字符和字符串
task hello << {
def name='a‘
println '单引号:${name}‘
println "双引号:${name}"
}
//输出结果
单引号:${name}
双引号:a
- 不管是单引号还是双引号都是String类型。
- 单引号没有运算能力。
集合,List集合,Map集合
//List集合
task printList << {
def numList=[1,2,3,4,5,6]
println numList[1]//访问第二个元
println numList[-1]//访问最后一个元素
println numList[-2]//访问倒数第二个元素
println numList[1..3]//访问第二个到第四个元素
}
//Map集合
task printMap << {
def map=["width":1080,"height":800]
println map["width"]//访问第二个元素
map.each{
println "Key:${it.key},Value:${it.value}"
}
}
闭包
闭包在很多语言中都有这种概念,在Java8中Lambda表达式也是闭包的形式。另外在javascript,kotlin中都有闭包的概念。如下示例:
定义方法customEach,它只有一个参数用于接收闭包代码块。如果只有一个参数,就用it代替。
task printClosure << {
//使用我们自定义的闭包
customEach{
println it
}
}
//自定义方法
def customEach(closure){
for(int i in 1..10){
closure(i)
}
}
定义方法eachMap,为闭包传递两个参数,key ,value,多个参数就不能用it了,必须要显示声明出来。
task printClosure << {
//使用我们自定义的闭包
eachMap{k , v ->
println "${k} is ${v}"
}
}
//自定义方法
def eachMap(closure){
def map=["name":"张三","age":18]
map.each{
closure(it.key,it.value)
}
}
自定义属性
//自定义 工具版本号
ext.build_tools_version = "25.0.2"
//引用版本号
buildToolsVersion build_tools_version
脚本即代码,代码也是脚本
这段代码是生成时间,可以用于打包文件名
def buildTime(){
def date=new Date()
def formattedDate=date.format('yyyyMMdd')
return formattedDate
}
Gradle插件
二进制插件
apply plugin:'java’
二进制插件就是实现了org.gradle.api.Plugin接口,需要有plugin id。
应用脚本插件
apply from:'version.gradle’
应用脚本插件就是执行了version.gradle一段脚本代码。
第三方插件
apply plugin:'com.android.application’
classpath ‘com.android.tools.build:gradle:1.5.0’
第三方发布的作为jar的二进制插件。
Android Gradle配置
默认配置
compileSdkVersion 28
buildToolsVersion '25.0.1'
defaultConfig {
applicationId "com.sort.demo"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
- applicationld:用于指定生成app的包名。
- minSdkVersion:最低支持的sdk版本。
- targetSdkVersion:用于配置基于哪个Android Sdk开发。
- compileSdkVersion:当前app编译版本。
- versionCode:用于配置App内部版本号,通常用于版本升级。
- versionName:用于配置App的版本名称,是让用户知道当前的版本。
- resConfigs:过滤语言
signingConfigs 签名信息配置
signingConfigs{
debug{
storeFile file("debug.keystore")
storePassword "password"
keyAlias "test"
keyPassword "password"
}
release{
storeFile file(“release.keystore”) //签名证书文件
storePassword “password” //签名证书文件密码
keyAlias “test” //签名证书秘钥别名
keyPassword “password” //签名证书秘钥密码
}
}
signingConfigs 需要写在defaultConfig 和buildTypes 前面,不然会编译报错。默认情况下debug模式签名已经配置好了,在$HOME/.android/debug.keystore。
buildTypes 构建类型
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
buildTypes的属性:
- applicationIdSuffix:应用id后缀
- versionNameSuffix:版本名称后缀
- minifyEnabled:是否混淆
- proguardFiles:混淆文件
- singningConfig:签名配置
- manifestPlaceholders:清单占位符
- shrinkResources:是否去除未使用的资源
- zipAlignEnable:是否使用zipalign工具压缩
- multiDexEnabled:是否拆分成多个Dex
- multiDexKeepFile:指定文本文件编译进主Dex文件中
- multiDexKeepProguard:指定混淆文件编译进主Dex文件中
sourceSets
每一个BuildType都会生成一个SourceSet,默认位置为src//。一个SourceSet包含源代码,资源文件等信息。针对不同的BuildType,可以单独为其指定Java源代码,res资源等。jniLibs目录下放置的是so库文件,直接放在目录下即可。如果so是在libs中,那就需要单独配置了,如下jniLibs.srcDirs=[‘libs’]。
sourceSets {
main {
res.srcDirs =
[
'src/main/res/google',
'src/main/res/baidu'
]
jniLibs.srcDirs = ['libs']
}
}
开发中我们会引用到aar这样的架包, 如果aar中的libs存在架包,也需要单独配置,配置如下:
repositories {
flatDir {
dirs 'libs'
}
}
使用abiFilters 过滤,其实这个可以不设置,这样编译时,就会将项目里所有依赖资源包里的so库都打到最终的apk里。但是有些平台,我们是不需要支持的,如果不删除的话,apk就臃肿了。如果那些so库是我们自己编译出来的,那可以直接在工程中删除对应so文件,但是如果是第三方提供的,就不好删除了,所以就需要使用abiFilters来过滤了。
defaultConfig {
ndk {
abiFilters 'armeabi','armeabi-v7a','arm64-v8a'
}
}
packagingOptions
用于解决文件冲突问题
packagingOptions {
exclude ’META-INF/LICENSE.txt‘
exclude ’META-INF/LICENSE‘
}
用于解决项目中的jar冲突
compile ('com.wdullaer:materialdatetimepicker:3.2.2') {
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'design'
}
简写方式
compile() { dep ->
['support-v4', 'support-v13', 'design'].each{ module -> dep.exclude module: module }
}
批量修改生成的apk文件名
applicationVariants.all { variant ->
variant.outputs.all { output -> // AS 3.0之后版本 each 改为 all
def fileName = "${buildTime()}_release.apk"
def outFile = output.outputFile
if (outFile != null && outFile.name.endsWith('.apk')) {
outputFileName = fileName// AS 3.0之后版本 output.outputFile 改为 outputFileName
}
}
}
动态生成版本信息
在开发中,一个工程中有多个module,每个module都有build.gradle配置,这些配置中的版本信息可以统一管理和维护。如下示例:
创建config.gradle内容如下
ext {
android = [
compileSdkVersion: 22,
minSdkVersion : 19,
targetSdkVersion : 22,
buildToolsVersion: '25.0.0',
versionCode : 1,
versionName : "1.0.1",
]
dependencies = [
supportV4: 'com.android.support:support-v4:22.2.1',
design : 'com.android.support:design:22.2.0'
]
}
在主工程中引入config.gradle
apply from: "config.gradle"
module中引用配置的内容
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
applicationId "com.example.demo"
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile rootProject.ext.dependencies.supportV4
compile rootProject.ext.dependencies.design
}
从git的tag中获取版本号
git describe --abbrev=0 --tags //获取当前tag名
动态获取版本名称
defaultConfig {
versionCode appVersionCode
versionName getAppVersionName()
}
从git tag中获取应用的版本名称
def getAppVersionName() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'cmd','git', 'describe', '--abbrev=0', '--tags'
standardOutput = stdout
}
return stdout.toString()
}
动态获取版本号
defaultConfig {
versionCode getAppVersionCode()
versionName getAppVersionName()
}
以git tag数量作为版本号
def getAppVersionCode(){
def stdout=new ByteArrayOutputStream()
exec {
commandLine 'cmd','git','tag','--list'
standardOutput = stdout
}
return split("\n").size()
}
隐藏签名文件信息
在团队开发中如果想隐藏签名信息,把正式包的时候不受影响。把信息存到主机环境变量中,既做到隐藏也能正常打包。
signingConfigs {
def appStoreFile=System.getenv("STORE_FILE")
def appStorePassword=System.getenv("STORE_PASSWORD")
def appKeyAlias=System.getenv("KEY_ALIAS")
def appKeyPassword=System.getenv("KEY_PASSWORD")
//当不能从环境变量里获取到签名信息的时候,就使用项目中带debug签名
if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
appStoreFile="debug.keystore"
appStorePassword="android"
appKeyAlias="androiddebugkey"
appKeyPassword="android"
}
release{
storeFile (appStoreFile)
storePassword appStorePassword
keyAlias appKeyAlias
keyPassword appKeyPassword
}
}
动态配置AndroidManifest文件
在我们做渠道统计的时候一般会这样做,在打包的时候会把${UMENG_CHANNEL}替换成渠道名。
<meta-data android:value="${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>
根据渠道名来表示渠道号,这样在渠道统计的时候就能统计不同的渠道。
productFlavors{
google{
manifestPlaceholders.put("UMENG_CHANNEL","google")
}
baidu{
manifestPlaceholders.put("UMENG_CHANNEL","baidu")
}
}
简写方式
productFlavors {
google { }
baidu { }
productFlavors.all { flavor ->
manifestPlaceholders.put("UMENG_CHANNEL", name)
}
}
自定义BuildConfig
比如:不同的渠道想实现打开不同的网页,可以这么做。
productFlavors {
google {
buildConfigField 'String','WEB_URL', ' http://www.google.com '
}
baidu {
buildConfigField 'String','WEB_URL', ' http://www.baidu.com '
}
}
比如:测试包用测试环境,正式包用正式环境。
buildTypes {
debug{
buildConfigField ' String ', ' WEB_URL ', ' http://www.ceshi.com '
}
release {
buildConfigField 'String','WEB_URL', ' http://www.zhengshi.com '
}
}
比如:测试包开启日志,正式包关闭日志
buildTypes {
debug{
buildConfigField 'bool', 'isDebug', 'true'
}
release {
buildConfigField 'bool', 'isDebug', 'false'
}
}
动态添加自定义的资源
res/values中的资源,不光可以在res/values中使用xml的方式定义,还可以在gradle中定义。这样不同的渠道引用的string是不同的。还可以使用id,bool,dimen,integer,color等类型定义value资源。在BuildType中也可使用。
productFlavors {
google {
resValue 'string','channel_tips','google渠道欢迎你'
}
baidu {
resValue 'string','channel_tips','baidu渠道欢迎你'
}
}
Java编译选项
compileOptions {
encoding=‘utf-8’//指定文件编码格式
sourceCompatibility=JavaVersion.VERSION_1_8 //源代码编译级别
targetCompatibility=JavaVersion.VERSION_1_8//生成Java字节码的版本
}
DEX选项配置
在apk打包的时候被dx命令优化成Android虚拟机可执行的DEX文件。默认情况下被dx分配内存是一个G,也就是1024MB。
dexOptions {
javaMaxHeapSize ‘4g‘ //执行dx最大堆内存
jumboMode true //忽略方法数限制的检查,apk无法再低版本的设备上面安装
threadCount 2 //dx使用的线程数
}
lintOptions
程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关。
lintOptions {
abortOnError false //即使报错也不会停止打包
checkReleaseBuilds false //打包release版本的时候进行检测
}