Gradle For Android笔记

Gradle For Android笔记

目录索引:

1.Gradle入门

1.1 gradle构建的生命周期

一个Gradle的构建通常有如下三个阶段:
1、初始化,项目的实例会在该阶段被创建。
2、配置,在该阶段,构建脚本会被执行,并为每个项目实例创建和配置任务。
3、执行,在该阶段,Gradle将决定那个任务会被执行。

1.2 构建配置文件

每一个基于Gradle构建的项目,都至少有一个build.gradle文件。android的构建文件中,有一些元素是必须的:
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}
这就是实际构建配置的地方,jcenter库作为依赖仓库,他是一个预配置的Maven仓库,不需要额外配置,
这里也可以添加自己的本地或远程仓库。

Android插件,每一个Android都应该申请该插件“apply plugin: 'com.android.application'”,
用于扩展Gradle构建的脚本能力。但不能与“apply plugin: 'com.android.library'”
下面的代码片段就是Adroid插件来定义的:
android {
    compileSdkVersion 27
    buildToolsVersion "27.0.1"
    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 27
    }
}

1.3 Gradle Wrapper

个人理解,根目录下./gradle/wrapper,GradleWrapper就是更新Gradle,配置下载地址的地方。

2.基本自定义构建

MyApp
  |____build.gradle
  |____settings.gradle
  |____app
        |____build.gradle

2.1.1 setting文件

settings文件在初始化阶段被执行,并定义了哪些模块应该包含在构建内。    对于Android应用,我们的settings.gradle文件是这样的:
include ':app'
如果包含多个Module,我们的settings.gradle文件是这样的
include ':app', ':other_module'
单模块不一定需要settings,但是多模块必须要有setting文件。

2.1.2 顶层构建文件

#实际构建在buildscript中
buildscript {
    #配置jcenter等代码仓库,我们在module中引用的依赖源于此仓库。
    repositories {
        jcenter()
    }
    #dependencies代码块用于配置构建过程中的依赖包
    #默认情况下,唯一被定义的依赖包是Gradle的Android插件
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }

}
#allprojects代码块可以用来声明那些需要被用于所有模块的属性
allprojects {
    repositories {
        jcenter()
    }
}

2.1.3 模块的构建文件

Module层的build.gradle可以覆盖顶层的build.gradle文件的任何属性。
Module的build.gradle示例如下:
//Android应用插件
apply plugin: 'com.android.application'

android {
    //当前编译API版本
    compileSdkVersion 26
    //构建工具和编译器使用的版本号
    buildToolsVersion "26.0.2"
    defaultConfig {
        //应用的Id,会覆盖AndroidManifest.xml中的package Name。例如两个不同的id安装到手机上会有两个应用。
        //可以通过Gradle构建动态设置不同的值,以区分版本类型,如付费、免费、生产、测试等。
        applicationId "com.ly.gradledemo"
        //支持最小sdk版本
        minSdkVersion 15
        //targetSdkVersion用于通知系统,该应用已经在某特定Android版本通过测试,
        // 从而操作系统不必启用任何向前兼容的行为。这和compileSdkVersion没任何关系
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    //签名文件配置
    signingConfigs {
        release {
            storeFile file("../keystore/key.jks")
            storePassword "tester"
            keyAlias "tester"
            keyPassword "tester"
        }
    }
    //构建任务类型,这里即使运行debug也会进行签名操作
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }

}
//依赖配置,从顶级配置文件中的jcenter仓库中选择添加的依赖包
dependencies {
    //依赖的文件,libs文件夹,包含内部所有的*.jar文件。
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //各种依赖支持包⬇️
    implementation 'com.android.support:appcompat-v7:26.+'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    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'
    configurations.all {
        resolutionStrategy.force 'com.android.support:support-annotations:26.1.0'
    }
}

2.2.1 BuildConfig和资源

SDK在17之后,构建时都会生成一个BuildConfig类,我们在代码中可以通过BuildConfig.DEBUG的值来判断构建的模式是release或debug。
我们可以通过以下配置切换常量:
//构建任务类型
    buildTypes {
        release {
            buildConfigField "String","BASE_URL","\"http://baidu.com\""
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            buildConfigField "String","BASE_URL","\"http://192.168.1.1/api\""
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }

同步之后可调用BuildConfig.BASE_URL常量,我们可以配置在网络模块中。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //可以写在网络模块中进行动态配置
        String url = BuildConfig.BASE_URL;
    }
}

2.2.2 项目属性

三种常用的定义属性方式:
1、ext代码块;
2、gradle.properties文件;
3、-P命令行参数;
下面的build.gradle中包含了这三种方式
ext{
    local = 'hello from build.gradle'
}
//定义任务,"<<"代表即时执行,配置时不执行。
task printProperties<<{
    println local
    println propertiesFile
    if (project.hasProperty('cmd')){
        println cmd
    }
}

在根目录下/gradle.properties文件中定义常量:

propertiesFile = Hello from gradle.properties

然后执行:

gradle printProperties -Pcmd='Hello from the command line'

得到打印结果如下:

> Task :gradle_demo:printProperties
hello from build.gradle
Hello from gradle.properties
Hello from the command line

我们也可以在Project的build.gradle中定义如下代码块,在Module的build.gradle中通过rootProject.ext.常量名,来调用

//Project的build.gradle
buildscript {...}
allprojects{...}
ext{
    compileSdkVersion = 26
    buildToolsVersion = "26.0.2"
}
//Module的build.gradle
apply plugin: 'com.android.application'
android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
}

3.依赖管理

3.1依赖仓库

Gradle支持三种不同的以来仓库:Maven、Lvy和静态文件或文件夹。
一个依赖通常是由三种元素定义的:group、nameversion。group指定了创建该依赖库的组织,name是依赖库的唯一标识,version指定了依赖库的版本号。

dependencies {
implementation 'com.google.code.gson:gson:2.3'
}
//完成的Groovy映射标识:
dependencies {
implementation group: 'com.google.code.gson',name:'gson',version:'1.9.0'
}

3.2 本地依赖

3.2.1 文件依赖

添加一个JAR文件作为依赖

dependencies {
//依赖一个jar文件
implementation files('libs/gson.jar')
//依赖libs文件夹
implementation fileTree('lib')
//依赖‘libs’文件夹中所有的*.jar文件,但不包括a.jar和b.jar
implementation fileTree(dir: 'libs',include: ['*.jar'], excludes: ['a.jar', 'b.jar'])
}

3.2.2 原生库依赖

用C或C++编写的依赖库可以被编译为特定平台的原生代码。这些依赖库通常包含几个.so文件,可用于所有平台。Android默认支持原生依赖,所需要做的就是在模块层创建一个jniLibs文件夹,然后为每个平台创建子文件夹,将.so放到合适的文件夹中。如下图:

app
|____AndroidManifest.xml
|____jniLibs
    |____armeabi
    |       |____nativelib.so
    |____armeabi-v7a       
    |       |____nativelib.so
    |......

如此约定不生效,那么可以在构建文件中设置相关设置:

android{
    sourceSets.main{
        jniLibs.srcDir 'src/main/libs'
    }
}

3.2.3 依赖项目

有两种依赖项目的方式,1、依赖module;2、依赖.aar文件。这里只对常用的方式1进行描述。

首先创建个名字为demoLib的Module,在其bulid.gradle中设置android插件

apply plugin: 'com.android.library'

然后在settings.gradle文件中添加此module名称,加入构建列表。

Include ':app',':demoLib'

最后在app.gradle中添加此模块作为依赖:

dependencies{
    implementation project(':demoLib')
}

4.创建构建Variant

...略,关于buildTypes、创建构建variant、签名的描述。
主要记下资源和manifest的优先顺序,这关系到资源覆盖的问题

Buildtype>Flavor>Main>Dependencies

5.管理多模块构建

简略概括内容:

1、Gradle构建流程,初始化,通过settings文件找到各个模块进行构建;

2、用相对于根目录的路径来声明模块的依赖,因为Gradle始终尝试从根目录找到依赖。

3.加速多模块构建,当一个project中有多个(apply plugin: 'com.android.application')时,
可以通过gradle.properties文件中的parallel属性进行并行构建。
org.gradle.parallel=true

6. 运行测试(略)

测试相关,不是重点不写了

7. 创建任务和插件

7.1 理解Groovy

对于android开发,我们只需要理解使用就可以了,不需要专门去学习这门语言。

7.1.1 简介

Groovy是从Java衍生出来的,运行在Java运行在Java虚拟机上的敏捷语言。

下面例子

ext{
    vname ="Andy"
    greeting = "Hello,$vname!"
    name_size = "Your name is ${name.size()} characters long."
}
task printStr<<{
    println vname
    println greeting
    println name_size
}

运行命令行:gradle printStr可得以下结果

> Task :gradle_demo:printStr
Andy
Hello,Andy!
Your name is 11 characters long.

下面的🌰动态执行代码:

def method = 'toString'
new Date()."$method"()

7.1.2 类和成员变量

在Groovy中,类和方法是公有的,成员变量是私有的。如果要获取成员变量,必须要创建实例:

class MyGroovyClass{
    String greeting
    String getGreeting(){
        return 'hello!'
    }
}
def instance = new MyGroovyClass()
println intance.greeting

7.1.3 方法

在Groovy方法中,最后一行通常默认返回,即使没有return,下面三种写法调用结果相等:

//调用:square(4),结果16;
public int square(int num){
    return num*num
}
//调用:square 4,结果16;
def square(def num){
    num*num
}
//调用:square 4,结果16;
def square ={num->
    num*num
}

7.1.4 Closures

Closures是匿名代码块,可以接受参数和返回值。它可以被视为变量,被当做参数传递给方法。

Closure square = {
    it * it
}
//square 4,结果16

如果没给明确的参数,Groovy会自动添加一个参数it,如果调用者没有指定任何参数,it则为空。android和dependencies代码块都是closures

7.1.5 集合

//创建list集合
List list = [1,2,3,4,5]
//迭代集合两种写法
list.each(){element->
    println element
}
list.each(){
    println it
}
//创建map集合
Map map = [a:1,b:2]
//可以通过get或者方括号获取value
map.get('a')
map['a']
map.a

7.2 任务

定义一个任务的写法:

task hello{
    println 'hello,world!'
}
task (hello) {
    println 'hello,world!'
}
task ('hello') {
    println 'hello,world!'
}
task.create(name:'hello') {
    println 'hello,world!'
}

Gradle构建中分三个阶段,初始化阶段、配置阶段、执行阶段。
如果定义了上述任务,他会在配置阶段执行,如果我们想在执行阶段执行,也就是手动是才执行,那么需要添加"<<"符号,通俗点说就是你在bulid的时候不会打印“hello,world!”。

task hello << {
    println 'hello,world!'
}

这里"<<"是doFirst()简写,如下:

task hello << {
    doFirst{
        println 'hello,world!'
    }
    doLast{
        
    }
}

7.3 APK重命名

AndroidStudio在3.0之后有改动,例

android.applicationVariants.all {variant ->
    variant.outputs.all { output ->
        def fileName = "demo_"+VERSION_NAME+"_"+variant.buildType.name+".apk"
        outputFileName = fileName
    }
}

生成apk文件名为:demo_1.0_debug.apk,其中"VERSION_NAME"是在gradle.properties文件中定义的常量,"debug"是打包时选择的构建类型。

8. 持续集成CI(略)

此章节关于Jenkins。

9. 高级自定义构建

9.1减少APK文件大小

9.1.1 ProGuard

ProGuard是一个Java工具,他可以缩减APK文件大小,还可以在编译期优化、混淆和预校验代码。

激活ProGuard,需要在Gradle中将minifyEnabled设置为true。
//getDefaultProguardFile('proguard-android.txt')从SDK的tools/proguard文件夹
//下的proguard-android.txt文件中获取Proguard默认设置

buildTypes {
        release {
            minifyEnabled true
            proguardFiles
            getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

因为ProGuard可以能会移除些需要用的代码,所以需要在==proguard-rules.pro==文件下添加如下代码,以保持文件不被混淆。

//保持MainActivity不被混淆
-keep public class MainActivity

9.1.2 缩减资源

通过shrinkResources为true设置成自动缩减资源。

如果资源意外被删除,可以在res/raw/下创建keep.xml文件,并在其中定义这些不需要被删除的资源。
buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles
            getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

【完】

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335