之前对于Gradle的理解就是内嵌在AS里帮项目编译编译,打打包,仅次而已。。。不得其精髓,就无法享受Gradle带给开发的快感。
什么是Gradle?
是一款很优秀的构建工具。
gradle官方文档
gradle中文文档
配置Gradle环境
不要以为它只是AS的附属品,只要配置了Gradle的环境,哪里又可以用,当然也包括服务端,干的事就很多了。
首先需要Java环境,JDK1.6以上,略。。。java -version
下载Gradle 官网下载https://gradle.org/,直接下载地址
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip
这里其实我拷贝的Gradle wrapper下gradle-wrapper.properties中的(等会会细说),下载all的是因为包含源代码,文档,实例等。
配置环境变量vim ~/.bash_profile 配置完成source ~/.bash_profile
GRADLE_HOME=/**/**/gradle
PATH=${PATH}:${GRADLE_HOME}/bin
export GRADLE_HOME PATH
gradle -v
//显示版本号即为成功
当然,可以try了
直接新建一个build.gradle文件,写入
task helloWorld<<{
println "hello wa wa !"
}
cd到同级目录下
//hw是驼峰简写, q 是日志级别quit ,println是quit级别
gradle -q hw/helloWorld
此处,应该可以简单想到AS gradle的工作原理了,实际里面是好多Gradle的任务。
其实我们在AS送使用的是Gradle Wrapper,wrapper 顾名思义就是在gradle上包括了一层,便于团队开发,比如我用的mac,你用的windows 他用的linux,wrapper可以帮我处理这些,我们可以使用相同的指令就可以了。gradle内置的gradle wrapper可以帮我们生成gradle wrapper
$ gradle wrapper
可以发现生成如下目录
gradle
--wrapper
--gradle-wrapper.jar
--gradle-wrapper.properties
gradlew
gradlew.bat
gradlew 和gradlew.bat就是linux和windows下的执行脚本
这个层级结构在我们的AS安卓项目里我们也可以看到,豁然开朗
gradle-wrapper.properties就是gradle的配置文件了,我们可以做一些设置,可以对比项目里
android.useDeprecatedNdk=true
org.gradle.daemon=true
zipStorePath=wrapper/dists
org.gradle.parallel=true
zipStoreBase=GRADLE_USER_HOME
org.gradle.caching=true
org.gradle.jvmargs=-Xmx5120M -XX\:MaxPermSize\=512m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
//下载gradle的路径
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip
org.gradle.configureondemand=true
distributionPath=wrapper/dists
//下载gradle解压后的主存储目录
distributionBase=GRADLE_USER_HOME
android.enableAapt2=false
android.useAndroidX=false
android.enableJetifier=false
看名字含义就差不多了;
当然我们也可以通过任务的形式配置wrapper
task wrapper(type:Wrapper){
gradleVersion = '4.8'
distributionUrl='https\://services.gradle.org/distributions/gradle-4.8-all.zip'
}
日志类似于项目中 e ERROR q QUIT w WARNING i INFO d DEBUG
logger.quit('这是一条重要的日志信息')
查看所有可执行的tasks
./gradlew tasks
./gradlew -help
./gradlew --refresh-dependencies assemble //强制刷新依赖库,防止缓存很有用我们项目中就发生过小伙伴电脑编译没问题,其他人从GitHub拉下来的就编译不通过
首先大家应该明白Project和Task的区别,Gradle可以包含多个Project,多模块项目,每个project包含多task,最后构成了整个gradle的构建,详情参照groovy官网、groovy教程
结合项目看看gradle的生命周期,根project下都有一个setting.gradle,build.gradle:
include ':application', ':modules:xcf_annotation_processor', ':modules:xcf_annotation', ':modules:xcf_annotation_complier', ':modules:xcf_annotation_api'
·
- 初始化创建Settings实例
- 解析settings.gradle 构造各个Project实例
- 解析每个Project对应的build.gradle,配置相应Project
差不多了,先来看几种任务的写法
//方式1
task name<<{//<<其实是doLast的意思,最后执行,{}闭包
description '添加描述'
}
//方式2
tasks.create(name){
description '添加描述'
doLast{
}
}
//也可以
name.doFrist{//开始执行
}
//自定义任务
class CustomTask extends DefaultTask{
@TaskAction
def doSelf(){
pritln 'do self'
}
}
def Task mTask =task execTask(type:CustomTask){
doFrist{ pritln 'doFrist' }
}
mTask.doLast{
pritln 'doLast'
}
./gradlew -q execTask
execTask.enable=false//可以禁用这个task
execTask.onlyIf{//满足某些条件才能执行
boolean
}
//多个任务排序
./gradlew task1 task2
指定顺序可以使用 task2.mustRunAfter(task1)
//任务依赖
task mTask1<<{
println 'hello'
}
task mTask2(dependsOn:mTask1){
doLast{
println 'world'
}
}
./gradlew -q mTask2
hello world
//还有另外一种写法,依赖多个task
task mTask3<<{
dependsOn:mTask1,mTask2
println 'end'
}
//每个task其实也是project的一个属性
project.hasdProperty('mTask3')
gradle是基于groovy的,groovy也是基于jvm虚拟机的一种动态语言,语法跟Java相似,懂Java,学习groovy没有任何障碍,完全兼容Java,可以直接写Java代码。接下来是一些基本用法
def str1='hello'
def str2="hello"
//行尾不必写分号
//方法
def method1(int a,int b){
a+b//此处注意可以省略return 默认最后一行就是返回
}
//两种调用方式
task mTask(){
method1(2,3)
method 1,2
}
//集合
task listTask{
def numList=[1,2,3]
numList[1]
numList[-1]
//it类似Java集合的迭代器
numList.each{ println it }
}
//map
task mapTask{
def map=['key':'value']
map.each{
println map['key']/map[key]
println 'key'${it.key}
}
}
//javabean
class Person {
private String name;
}
task beanTask{
Person p=new Person
p.name="hello"
println "name:${p.name}"
}
//自定义属性,可以是project可以是task
ext.age=18
ext{
sex='famle'
phone='1234445'
}
task task1<<{
println("phone:${phone}")
}
Gradle插件
为什么我们几乎什么也不用关心就能在Android studio中构建我们安卓项目,这是因为Gradle默认实现了很多有用的插件;这些是我们常见的插件,他们帮我们做了哪些平常我们不需要深入关心的工作,比如编译,测试,打包,资源文件目录等等。
buildscript{}块是在有个项目构建前,为项目准备初始化相关配置的地方,配置好依赖我们就可以使用插件了
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'com.growingio.android'
比如:com.android.application就是一个Android相关的插件,引入这个我们就可以使用Android{}代码块的先关配置,来帮助我们生成编译打包我们的Android工程;
简单自定义插件,通过下面简单的自定义插件,可以帮助大家了解Android插件的工作方式,内部帮助我们实现了很多的task。
在build文件中
class CustomPlugin extends Plugin{
void app(){
project.task("task1")<<{
println("hello task1")
}
}
}
apply plugin :CustomPlugin
接下来可以分别简单介绍一下Java和Android插件:
Java插件
apply plugin : 'java'
上述眼熟的代码起始就是大家新建Java模块时候依赖的Java插件,他来帮助我们自动构建我们的Java项目
//添加依赖的时候告诉gradle去哪里找到这些依赖,设置依赖仓库
repositories{
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
maven { url "https://dl.bintray.com/thelasterstar/maven/" }
jcenter()
}
}
//告诉gradle我们依赖什么,group,name,version以冒号隔开
dependencies {
implementation 'com.alibaba:fastjson:1.1.46'
}
注:
api 跟2.x的compile类似
implementation 这个最常用,在当前模块内起作用,比如B模块依赖了Gson,A模块依赖B模
有了上述构建的基本的以来就有了,对于项目本身的文件资源默认是在sourceSet中配置
//这个就是Java工程的默认目录了,当然如果是需要自定义目录结构,就可以更高这里
sourceSets{
main{
java{
srcDir 'src/java'
}
resources{
srcDir 'src/resources'
}
}
}
//大家可以打印一下,都有哪些属性
task printSourceSet{
sourceSets.all{
println name
}
}
当然这些都是常用的,要想编译运行我们的Java项目,还依赖于我们的tasks,默认的Java插件都帮我们实现完了,如果大家想看有哪些任务:./gradlew tasks查看所有的任务;
还有几个常见属性和常用的任务:
//编译Java源文件使用的Java版本
sourceCompatibility Javaversion
//编译生成类的Java版本
targetCompatibility Javaversion
//生成类库的目录
libsDir File
//打jar包
task publishJar(type:Jar)
artifacts{
archives publishJar
}
//最有用的是上传任务,可以打完包配置直接上传maven,jcenter,详情可自行百度
uploadArchIves{
}
Android插件
这应该是我们平时使用和见的最多的
apply plugin: "com.android.application"
里面android{}就是我们配置Android gradle的总入口
android {
compileSdkVersion xx
buildToolsVersion 'xxx'
defaultConfig{
applicationId 'xxx'
xxxx
xxxx
//defaultConfig中可以显示的配置singningConfigs,否则会根据buildTypes自动release或者debug
signingConfig singningConfigs.debug
}
//签名配置信息
singningConfigs{
release{
storeFile file('xxxxxx.keystore')
storePassword 'xxx'
keyAlias 'xxx'
keyPassword 'xxx'
}
debug{
storeFile file('xxxxxx.keystore')
storePassword 'xxx'
keyAlias 'xxx'
keyPassword 'xxx'
}
}
buildTypes{
release{
// 此处可以配置签名配置
signingConfig singningConfigs.debug
// 是否需要混淆
minifyEnable boolean
//设置为.debug那么打出的包名就是applicationId.debug
applicationIdSuffix 'xxx'
mutiDexEnable boolean
proguardFiles file
//清除未使用的资源
shrinkResources boolean
//Android为我们提供的整理apk的工具,更快的读写apk中的资源,降低内训的使用,建议开启
zipaligin boolean
}
debug{}
}
//配置渠道包,常用
productFlavors{
google{}
huawei{}
}
}
Android项目gradle的加载流程setting.gradle--->root.gradle--->setting中配置的project
所以我们在root.gradle做一些通用的配置
buildscript {//gradle脚本执行的依赖,root中添加作用其他subject/moudles
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
allprojects {//项目本身需要的依赖,对所有projects都起作用
repositories {
mavenCentral()
}
}
设置一个gradle基类,这个在多module中非常实用,创建common.gradle在根目录下:
android {
compileSdkVersion rootProject.ext.compileSdk
buildToolsVersion rootProject.ext.buildTools
defaultConfig {
minSdkVersion rootProject.ext.minSdk
targetSdkVersion rootProject.ext.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
dexOptions {
javaMaxHeapSize "4g"
}
packagingOptions {
exclude '.readme'
exclude 'LICENSE.txt'
exclude 'META-INF/dependencies.txt'
}
}
//在其他模块中直接apply from 就可以继承上面所有的属性,不需要重复写
apply from: rootProject.file('common.gradle')//其他目录就可以xxxx/xxx/common.gradle
android {
defaultConfig {
applicationId rootProject.ext.packageName
versionCode 1
versionName "1.0"
}
}
//还有一种写在公共模块的方式在root.gradle
configure(project(':app').subprojects) {//相当于放在app下的build.gradle
apply plugin :'xxx'
android {
defaultConfig{}
}
dependencies{}
}
抽取公共常量constant.gradle
ext {
android_const = [
compileSdk : 28,
minSdk : 16,
targetSdk : 26,
support : "27.1.1",
buildTools : "28.0.3",
packageName: "com.xiachufang"
]
dep_const = [
glide : "3.6.0",
okio : "1.4.0",
okhttp : "2.4.0",
fabric : "2.4.0",
logansquare: "1.1.0",
dagger : "2.0.1",
butterknife: "7.0.1",
retrofit : "2.0.0-beta2",
greendao : "2.0.0"
]
app_dep = [
"appcompat-v7" : 'com.android.support:appcompat-v7:' + android_const.support,
"design" : 'com.android.support:design:' + android_const.support,
"okio" : 'com.squareup.okio:okio:' + dep_const.okio,
"okhttp" : 'com.squareup.okhttp:okhttp:' + dep_const.okhttp,
"glide" : 'com.github.bumptech.glide:glide:' + dep_const.glide,
"butterknife" : 'com.jakewharton:butterknife:' + dep_const.butterknife,
"rxjava" : 'io.reactivex:rxjava:1.0.10',
"rxandroid" : 'io.reactivex:rxandroid:0.24.0',
"retrofit" : 'com.squareup.retrofit:retrofit:' + dep_const.retrofit,
"converter-gson" : 'com.squareup.retrofit:converter-gson:' + dep_const.retrofit,
"adapter-rxjava" : 'com.squareup.retrofit:adapter-rxjava:' + dep_const.retrofit
]
}
//可以在每个模块中apply from:
//也可以在root.gradle中apply,然后在其他模块中
apply plugin: 'java'
dependencies {
compile rootProject.ext.app_dep.rxjava
}
说一个常用的属性BuildConfig,大家可能都使用过,来判断是都是Debug
,这个文件是不能更改的,是gradle帮我们生成的,那么我们想扩展先关字段怎么办?如果能扩展还是很方便的
//我们使用这个扩展过线上和debug的地址
productFlavors{
google{
buildConfigField 'String' 'WebUrl' 'www.google.com'
//添加自定义资源
resValue 'String' 'chanel_tips' 'google 欢迎你'
}
huawei{
buildConfigField 'String' 'WebUrl' 'www.huawei.com'
}
}
其他扩展
//Java编译版本编码格式等等
compileOptions{}
adbOptions{}
testOptions {}
dexOptions{
//配置dex命令时分配最大堆内存
javaMaxHeapSize '4g'
// Sets the maximum number of DEX processes
maxProcessCount 8
}
packagingOptions {
exclude '.readme'
exclude 'LICENSE.txt
}
defaultConfig{
//国际化只打中文资源
resConfigs 'zh'
}
多渠道构建
在gradle Android plugin中定义了一个Build variant的感念,翻译为构建变体。实际就是构建apk,实际也就是Build type+Product flavor=Build variant,Build type是构建类型,Product flavor就是多渠道配置,Build type也就是release,debug,自定义类型等,之前说过
productFlavors{
google{
applicationId ''//指定渠道包名
manifestPlaceholders.put("UMENG_CHANNEL","google")//修改
AndroidManifest的值
// app名称替换
manifestPlaceholders = [app_name:"@string/app_name"]
multiDexEnable//设置是否支持多dex
proguardFiles//单独混淆
signingConfig//单独的签名配置
versionCode
verisonName
}
huawei{}
}
//设置维度
flavorDimensions 'abi' ,'version'
productFlavors{
free{
dimensions 'version'
}
pay{
dimensions 'version'
}
x86{
dimensions 'abi'
}
arm{
dimensions 'abi'
}
}
//将会构建出x86free x86pay armfree armpay的包
常用的结合的例子
获取keystorePropertiesFile
// Creates a variable called keystorePropertiesFile, and initializes it to the
// keystore.properties file.
def keystorePropertiesFile = rootProject.file("keystore.properties")
// Initializes a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()
// Loads the keystore.properties file into the keystoreProperties object.
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
signingConfigs {
config {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
...
}
...
FileProvider骚操作
android {
defaultConfig {
def filesAuthorityValue = applicationId + ".files"
manifestPlaceholders = [filesAuthority: filesAuthorityValue]
buildConfigField("String",
"FILES_AUTHORITY",
"\"${filesAuthorityValue}\"")
}
}
}
//在AndroidManifest中使用
...
...
...
//在Java代码中
Uri contentUri = FileProvider.getUriForFile(getContext(),
BuildConfig.FILES_AUTHORITY,
myFile);
根据git tag来生成版本号
def getVersionCode = { ->
try {
def code = new ByteArrayOutputStream()
exec {
commandLine 'git', 'tag', '--list'
standardOutput = code
}
return code.toString().split("\n").size()
}
catch (ignored) {
return -1;
}
}
def getVersionName = { ->
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'describe', '--tags', '--dirty'
standardOutput = stdout
}
return stdout.toString().trim()
}
catch (ignored) {
return null;
}
}
android {
defaultConfig {
versionCode getVersionCode()
versionName getVersionName()
}
}
通过uploadArchives上传maven
apply plugin: 'maven'
apply plugin: 'signing' //使用signing plugin做数字签名
//定义GroupID和Version,
//ArtefactID会自动使用Project名
group = 'com.github.xxx'
version = '1.0.0'
repositories {
mavenCentral();
}
artifacts {
archives file('xxx.aar')
}
signing {
sign configurations.archives
}
uploadArchives {
repositories {
mavenDeployer {
//为Pom文件做数字签名
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
//指定项目部署到的中央库地址,UserName和Password就是Part 1中注册的账号。
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: Username, password: Password)
}
snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
//构造项目的Pom文件,不要遗漏必填项
pom.project {
name project.name
packaging 'aar'
description 'xxxxxx'
url 'https://github.com/xxx'
scm {
url 'scm:[email protected]:xxx.git'
connection 'scm:[email protected]:xxx.git'
developerConnection '[email protected]:xxx.git'
}
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'xxxx'
}
}
developers {
developer {
id 'xxx'
name 'xxx'
email '[email protected]'
}
}
}
}
}
}