Gradle介绍
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。对Android开发人员来说,就是AS引入的Android工程的管理,帮我们做了依赖,打包,部署,发布,各种渠道的差异管理等工作(官方的解释比较绕口,我就这么通俗的理解)。
举个例子,以前用eclipse,那是导入一个网上的下载的module还需要一步步的import,但使用Android Studio,Gradle很贴心的完成了这个繁杂的工作,而且往往只需要添加一句话,这就是效率,可以让开发人员专心写代码。
Gradle文件
一个Android的AS工程通常有几个地方涉及Gradle文件:内置的Wrapper、settings.gradle、build.gradle(Project)、build.gradle(Module):
我们看到,一个主Project的Gradle文件管理全局,另外每个Module都会对应有一个Gradle文件。
内置的Wrapper
Wrapper是对Gradle的一层包装,便于在团队开发过程中统一Gradle构建的版本号,这样大家都可以使用统一的Gradle版本进行构建。先看gradle-wrapper.properties这个文件的作用:
#Fri May 18 13:56:55 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
我们其实最关心的应该是distributionUrl这个属性,他是下载Gradle的路径。
我们经常在导入项目的时候一直会停留在这个界面加载Gradle界面,短则几分钟,长则30分钟,甚至更长,这是为什么?其实原因就是你常用项目的Gradle版本跟你新导入项目的Gradle版本不一致造成的,通常可以这么解决:
- 网速好或者科学上网的时候,由它自己去下载,不过下载时间有长有短,不能保证;
- 把其他项目的gradle-wrapper.properties文件替换掉你要导入项目的该文件,特别是多人协作开发,因个人电脑环境的问题导致的,这样处理非常有效。
settings.gradle
settings.gradle文件其实是用于初始化以及工程树的配置的,放在根工程目录下。
在Gradle众多工程是通过工程树表示的,相当于我们在Android Studio看到的Project和Module概念一样。根工程相当于Android Studio的Project,一个根工程可以有很多自工程,也就是很多Module,这样就和Android Studio定义的Module概念对应上了。
include ':app',':swipemenulib',':GaiaLibrary',':tuling-master',':speechutils-master','nineoldandroids',':lib_csmart'
我们可以看到这个项目我们添加了7个module,一一对应,如果你的项目添加了项目依赖,那就会出现在这个文件当中。
build.gradle(Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
- buildscript:buildscript中的声明是gradle脚本自身需要使用的资源。可以声明的资源包括依赖项、第三方插件、maven仓库地址等;
- repositories:顾名思义就是仓库的意思啦,而jcenter()、maven()和google()就是托管第三方插件的平台;
- dependencies:当然配置了仓库还不够,我们还需要在dependencies{}里面的配置里,把需要配置的依赖用classpath配置上,因为这个dependencies在buildscript{}里面,所以代表的是Gradle需要的插件;
- allprojects:allprojects块的repositories用于多项目构建,为所有项目提供共同所需依赖包。而子项目可以配置自己的repositories以获取自己独需的依赖包。
buildscript和allprojects的作用和区别,buildscript中的声明是gradle脚本自身需要使用的资源,就是说他是管家自己需要的资源;而allprojects声明的却是你所有module所需要使用的资源,就是说如果每个module都需要用同一个第三库的时候,你可以在allprojects里面声明。
gradle clean执行此处定义的task,该任务继承自Delete,删除根目录中的build目录,相当于执行Delete.delete(rootProject.buildDir),其实这个任务的执行就是可以删除生成的Build文件的,跟Android Studio的clean是一个道理。
build.gradle(Module)
该文件是整个项目的关键,能让Gradle发挥多大的用处,就在于此配置文件。
apply plugin:'×××'
apply plugin: 'com.android.application'
或
apply plugin: 'com.android.library'
基本上每个项目第一句都是一样,若是主Module,则用"application",库的话,就用"library"。通过查阅资料,这种叫做引入Gradle插件,而Gradle插件大致分为分为两种:
- apply plugin:'×××':叫做二进制插件,二进制插件一般都是被打包在一个jar里独立发布的,比如我们自定义的插件,再发布的时候我们也可以为其指定plugin id,这个plugin id最好是一个全限定名称,就像你的包名一样;
- apply from:'×××':叫做应用脚本插件,其实这不能算一个插件,它只是一个脚本。应用脚本插件,其实就是把这个脚本加载进来,和二进制插件不同的是它使用的是from关键字.后面紧跟的坫一个脚本文件,可以是本地的,也可以是网络存在的,如果是网络上的话要使用HTTP URL。
android{}
android {
compileSdkVersion 27
}
android{}是Android插件提供的一个扩展类型,可以让我们自定义Android Gradle工程,是Android Gradle工程配置的唯一入口。
compileSdkVersion
compileSdkVersion是编译所依赖的Android SDK的版本,这里是API Level。
defaultConfig{}
defaultConfig是默认的配置,它是一个ProductFlavor。ProductFlavor允许我们根据不同的情况同时生成多个不同的apk包。
defaultConfig {
applicationId "com.cchip.***"
minSdkVersion 18
targetSdkVersion 27
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
multiDexEnabled false
}
- applicationId:配置我们的包名,包名是app的唯一标识,其实他跟AndroidManifest里面的package是可以不同的,他们之间并没有直接的关系。
- minSdkVersion:是支持的Android系统的api level,这里是18,也就是说低于Android 18版本的机型不能使用这个app。
- targetSdkVersion:表明我们是基于哪个Android版本开发的,这里是27。
- versionCode:表明我们的app应用内部版本号,一般用于控制app升级,当然我在使用的bugly自动升级能不能接受到升级推送就是基于这个。
- versionName:表明我们的app应用的版本名称,一般是发布的时候写在app上告诉用户的(特别是在某个版本上修复了一个重要的Bug,客户还一直纠结着没有修复,此时,首先对下这个版本号,避免双方不在一个频道上讨论问题)。
- multiDexEnabled:用于配置该BuildType是否启用自动拆分多个Dex的功能。一般用程序中代码太多,超过了65535个方法的时候。
- ndk{}:多平台编译,生成有so包的时候使用,包括四个平台'armeabi', 'x86', 'armeabi-v7a', 'mips'。一般使用第三方提供的SDK的时候,可能会附带so库。
buildType
构建类型,在Android Gradle工程中,它已经帮我们内置了debug和release两个构建类型,两种模式主要车别在于,能否在设备上调试以及签名不一样,其他代码和文件资源都是一样的。
buildTypes {
release {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
zipAlignEnabled true
ndk {
abiFilters "armeabi", "x86"
}
}
debug {
ndk {
abiFilters "armeabi", "x86"
}
}
}
- minifyEnabled:是否混淆
- shrinkResources:是否去除未利用的资源,默认false,表示不去除
- multiDexKeepProguard:指定混淆文件编译进主Dex文件中
- proguardFiles:混淆文件
signingConfigs
签名配置,一个app只有在签名之后才能被发布、安装、使用,签名是保护app的方式,标记该app的唯一性。如果app被恶意删改,签名就不一样了,无法升级安装,一定程度保护了我们的app。而signingConfigs就很方便为我们提供这个签名的配置。
signingConfigs {
debug {
keyAlias 'cchip'
keyPassword '*******'
storeFile file('cchip.keystore')
storePassword '*******'
}
}
- keyAlias签名证书中秘钥别名
- keyPassword签名证书中改密钥的密码
- storeFile签名文件
- storePassword签名证书文件的密码
applicationVariants.all{}
自定义导出的APK名称,默认android studio生成的apk名称为app-debug.apk或者app-release.apk,当有多个渠道的时候,需要同时编出50个渠道包的时候,就麻烦了,不知道谁是谁了。这个时候,就需要自定义导出的APK名称了,不同的渠道编出的APK的文件名应该是不一样的。
applicationVariants.all { variant ->
variant.outputs.all { output ->
def oldFile = output.outputFileName
def releaseApkName = ''
if (variant.buildType.name.equals('release')) {
if (variant.productFlavors[0] != null) {
releaseApkName = 'C Baby-V' + variant.productFlavors[0].name + '-v' + versionName + '-' + getDate() + '.apk'
} else {
releaseApkName = 'C Baby-V' + versionName + '-' + getDate() + '.apk'
}
output.outputFileName = new File("", releaseApkName)
}
if (variant.buildType.name.equals('debug')) {
}
}
}
def getDate() {
return new Date().format('MMddHHmm')
}
dependencies{}
项目依赖包管理,这个功能非常强大:
首先第一句compile fileTree(include: ['.jar'], dir: 'libs')*,这样配置之后本地libs文件夹下的扩展名为jar的都会被依赖,非常方便。
如果你要引入某个本地module的话,那么需要用compile project('×××')。
如果要引入网上仓库里面的依赖,我们需要这样写compile group:'com.google.code.gson',name:'gson',version:'2.8.1',当然这样是最完整的版本,缩写就把group、name、version去掉,然后以":"分割即可。
compile 'com.google.code.gson:gson:2.8.1'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.jakewharton:butterknife:7.0.0'
compile project(path: ':lib_csmart')
compile project(path: ':GaiaLibrary')
compile 'com.alibaba:fastjson:1.2.9'
compile 'com.android.volley:volley:1.0.0'
compile 'com.google.code.gson:gson:2.8.1'
//登陆
compile 'com.ali.auth.sdk:alibabauth_core:1.4.3@jar'
compile 'com.ali.auth.sdk:alibabauth_ui:1.4.3@aar'
compile 'com.ali.auth.sdk:alibabauth_ext:1.4.3@jar'
//安全基础
compile 'com.taobao.android:securityguardaar3:5.1.81@aar'
//Mtop
compile 'com.taobao.android:mtopsdk_allinone_open:1.3.0@jar'
//applink
compile 'com.taobao.android:alibc_applink:2.0.0.2@jar'
}
或
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support:support-vector-drawable:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
implementation 'com.umeng.analytics:analytics:latest.integration'
implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar'
implementation project(':swipemenulib')
implementation project(':GaiaLibrary')
implementation project(':tuling-master')
implementation project(':lib_csmart')
compile 'com.umeng.analytics:analytics:latest.integration'
}
但是到了gradle3.0以后build.gradle中的依赖默认为implementation,而不是之前的compile。
常用功能配置
定制APP
在Eclipse时代,定制APP,除了修改包名,可以还要修复因修改包名引起的编译异常问题,但在AS工程中,定制APP只需修改下Gradle中的:applicationId
applicationId "com.cchip.***"
只需修改applicationId,而不用理会Manifest中的package="com.cchip.XXX"
替换AndroidManifest中的占位符
举个例子,在AndroidManifest文件中,我们将盟统计的key值指定为一个占位符:
<meta-data
android:name="UMENG_APPKEY"
android:value="${UMENG_APPKEY}" />
在build.gradle文件中,这里介绍3种方法去替换该占位符
- 接收gradlew assemble命令输入的自定义参数的值
manifestPlaceholders = [
UMENG_APPKEY: "\"" + UMENG_APPKEY_PARA + "\""
]
- 使用string文件的值
manifestPlaceholders = [UMENG_APPKEY:"@string/UMENG_APPKEY"]
- 使用gradle.properties文件的值:
# 友盟统计
UMENG_APPKEY=**************
多渠道打包
多渠道打包用得比较少,不涉及利益分配问题,都固定死一个来源,反正无所谓。但涉及利益分配的时候,还是需要的:
- 配置AndroidManifest.xml
以友盟渠道为例,渠道信息一般都是写在 AndroidManifest.xml文件中:
<meta-data android:name="UMENG_CHANNEL"
android:value="Baidu" />
如果不使用多渠道打包方法,那就需要我们手动一个一个去修改value中的值,xiaomi,360,qq,wandoujia等等。使用多渠道打包的方式,就需要把上面的value配置成下面的方式:
<meta-data android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
其中${UMENG_CHANNEL_VALUE}中的值就是你在gradle中自定义配置的值。
- 在build.gradle设置productFlavors
写法如下:
productFlavors {
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
}
Baidu {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Baidu"]
}
qq {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qq"]
}
360 {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "360"]
}
}