Gradle 与 Android的三生三世:是我构建了你,你必将依赖于我

Android应用的构建过程是一个复杂的过程,涉及到很多工具。首先所有的资源文件都会被编译,并且在一个R文件中引用,然后Java代码被编译,通过dex工具转换成dalvik字节码。最后这些文件都会被打包成一个APK文件,此应用被最终安装到设备之前,APK会被一个debug或者release的key文件签名。

以上步骤人工操作着实繁琐,2013推出了Gradle,作为Android首选的构建系统,Gradle设计的方式使得它可以很容易地扩展构建和插入到现有的构建中,提供了一套类Groovy的DSL语言,用于申明构建和创建任务,让依赖管理更简单。以此篇博文记录归纳Gradle 与 Android相关联的知识点。

本篇文章涉及到的知识点:

  • Gradle入门基础知识点(基础理论、配置文件、项目结构)
  • 基本自定义结构(Gradle文件、任务)
  • 依赖管理(仓库、本地依赖)
  • 构建variant(构建类型创建、签名配置)
  • 测试配置、常用命令、Task

一. Gradle入门

若想用Gradle构建Android项目,需要创建一个脚本,此脚本被称为build.gradle。(Gradle中有约定优于配置的原则,即为设置和属性提供默认值,使得它比Ant、Maven更容易上手)

Gradle构建脚本并非基于传统的XML文件,而是Groovy的领域专业语言(DSL)。Groovy是一种基于JVM的动态语言,优势更加显著。这并非意味着在此之前我们需要学习Groovy,若了解Java,其学习难度不大,代码更是易读,后续若需要创建自己的任务和插件,可采用Groovy自定义插件。

1. 基础理论

(1)项目和任务

Gradle中最重要的两个概念是项目任务每一次构建都包括至少一个项目,每一个项目又包括一个或多个任务。 每个build.gradle 文件都代表一个项目,任务定义在构建脚本里。当初始化构建过程时,Gradle会基于build文件组装项目和任务对象。

  • 一个任务对象包含一系列动作对象,这些动作对象之后会按顺序执行。
  • 一个单独的动作对象就是一个待执行的代码块,它和Java中的方法类似。

(2)构建生命周期

执行一个Gradle构建最简单的形式是至执行任务中的动作,因为这些任务会依赖于其他任务。

为了简化构建过程,构建工具会新建一个动态的模型流,即Directed Acyclic Graph(DAG)。意味着所有的任务都会被一个接一个地执行,一旦一个任务被执行,就不会被再次执行。注意一开始说的优先配置原则,没有依赖的任务会被优先执行。在构建的配置阶段会生成依赖生成图,一个Gradle的构建通常有以下三个阶段:

  • 初始化(Initialization):项目实例会在该阶段被创建。如果一个项目有多个模块,并且每一个模块都有其对应的build.gradle 文件,则会创建多个项目实例。
  • 配置(Configuration):该阶段构建脚本会被执行,并为每个项目实例创建和配置任务。
  • 执行(Execution):该阶段Gradle将决定哪个任务会被执行,这取决于开始该次构建的参数配置和该Gradle文件的当前目录。

这里写图片描述


2. 构建配置文件

每一个基于Gradle构建的项目,都应该至少有一个build.gradle 文件,在Android构建文件中,以下元素是必需的:

buildscript{
    repositories{
        jcenter()   
    }
    dependencies{
        classpath 'com.android.tools.build:gradle:1.2.3'    
    }
}

以上是实际构建配置的地方,在repositories代码块中,JCenter库被配置为整个构建过程的依赖仓库。JCenter是一个预配置的maven仓库,不需要额外的配置。当然在Gradle中有多个仓库可选择,可以添加自己本地或远程仓库。

构建脚本代码块在Android构建工具上定义了一个依赖,就是Android插件的来源,Android插件提供了构建和测试应用所需要的一切。每一个Android项目都应该申请该插件:

apply plugin : 'com.android.application'

插件用于扩展Gradle构建脚本的能力,在一个项目中应用一个插件,该项目就可以使用该插件预定义的一些属性和任务。

如果你正在构建一个依赖库,需要声明 ‘com.android.library’ ,而不是’com.android.application’ 。
不能在一个模块中同时使用它们,会导致构建错误。一个模块可以是一个Android应用模块,或者是一个Android依赖模块,但不可两者皆是。

在使用Android插件时,不仅可以配置针对Android的特殊约定,还可以生成只应用于Android的任务,下面的代码是由插件来定义的,可以配置在每个项目中:

android{
    compileSdkVersion 22
    buildToolsVersion "22.0.1"
}

以上是配置Android特殊约定参数的一部分,Android插件为Android开发需求提供了一整套DSL。参数含义是指编译SDK版本和构建工具版本。在build.gradle 文件还有其他自定义特性,后续介绍。


3. 项目结构

这里的IDE主要以Android Studio为主,一个简单的应用文件夹结构如下图:

  • MyApplication
    • build.gradle
    • settings.gradle
    • app
      • build.gradle
      • build
      • libs
      • src
        • main
          • com.package.application
        • res
          • drawable
          • layout
          • etc.

Gradle项目通常会在根目录下创建一个gradle脚本文件,使得在后续阶段新增模块变得更加简单,所有的应用源代码都在app文件夹下。

Gradle使用了一个叫作源集(source set)的概念,官方文档解释为:一个源集就是一组源文件,它们会被一起执行和编译。对于一个Android项目而言,main就是一个源集,它包含了所有测试相关的原代码都放在一个独立的源集中,该源集称为AndroidTest,只包含测试代码。

下表是一个Android应用中主要的文件夹的简短概述:

目录 内容
/src/main/java 内容的源代码
/src/main/res 应用相关资源(drawables、layouts、strings等)
/libs 外部库(.jar或者.aar)
/build 构建过程的输出

4. Gradle Wrapper

Gradle工具版本不断迭代,新版本可能会打破向后兼容性,而使用Gradle Wrapper可以避免这个问题,并保证构建是可重复的。Gradle Wrapper为Windows操作系统提供了一个batch文件,为其他操作系统提了一个shell脚本,运行脚本时,若Gradle不存在会自动下载和使用。原理是每个需要构建应用的开发者或自构建系统可以仅仅运行Wrapper来搞定剩余部分。(运行Gradle Wrapper和Gardle没什么不同)

(1)获取Gradle Wrapperer

每个新的Android项目都会包含Gradle Wrapper,即创建一个项目就可获得其必要文件,以下是有关Wrapper的文件:

  • myapplication/
    • gradlew
    • gradlew.bat
    • gradle/wapper/
      • gradle-wrapper.jar
      • gradle-wrapper.properties

这里写图片描述

以上可知Gradle Wrapper有三个部分:

  • Windows上的batch文件,其他系统上的shell脚本。
  • batch文件和shell脚本需要用到的JAR文件。
  • 一个properties文件。

gradle-wrapper.properties文件包含了参数配置,并能决定使用哪一个Gradle 版本:

#Thu Nov 09 18:58:35 CST 2017
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

如果想要使用内部分发的自定义Gradle版本,可以修改URL路径,这意味着你使用的应用或者依赖库都可以有一个不同的Gradle URL。

当项目使用的不是最新的Gradle版本时,AS编译器将会提示更新版本,其中原理是AS更改了gradle-wrapper.properties的配置并触发了一次构建,下载最新版本。




二. 基本自定义构建

1. 理解Gradle文件

在前面已经提过,使用AS创建一个新项目时,会默认生成3个Gradle文件,其中 build.gradlesettings.gradle位于项目的根目录。另外一个build.gradle文件在Android app 模块内被创建,下图是项目中Gradle文件的位置:

  • MyApplication
    • build.gradle
    • settings.gradle
    • app
      • build.gradle

这里写图片描述

(1)settings文件

对于一个只包含一个Android应用的新项目来说,settings.gradle文件内容如下:

Include':app'

settings文件在初始化阶段(前面讲过的Gradle构建三阶段)被执行,并且定义了哪些模块应该包含在构建内。本例中app模块被包含在内,单模块项目并不一定需要setting文件,但是多模块项目必须要有setting文件,否则Gradle不知道哪个模块应包含在构建内。

(在其背后原理,Gradle会为每一个setting文件创建Setting对象,并调用该对象的相关方法,稍作了解即可)

(2)项目的build.gradle

在项目中,所有模块的配置参数都应该在项目的build.gradle文件中配置,默认情况下包含以下两个代码块:

buildscript {

    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

前面介绍过,实际构建配置在buildscript代码块repositories代码块将JCenter配置成一个仓库,一个仓库意味着一系列的依赖包,或者说是应用和依赖项目中可使用的一系列可下载的函数库。(JCenter是一个有名的Maven库)

dependencies代码块用于配置构建过程中的依赖包这也意味着不能将应用或依赖项目所需要的依赖包包含在项目构建文件build.gradle中。默认情况下,唯一被定义的依赖包是Gradle的Android插件,每个Android模块都需要有Android插件,借助它来执行Android相关的任务。

allprojects代码块可用来声明哪些需要被用于所有模块的属性。(甚至可以在其中创建任务,这些任务最终会被运用到所有模块)

(3)模块的build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildtoolsversion "22.0.1"

    defaultConfig {
        applicationId "com.example.lemon.testapplication"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*jar'])
    compile 'com.android.support:appcompat-v7:26.0.0'
}

下面对以上3个部分进行详细分析

a. 插件

第一行用到了Android应用插件,该插件在项目构建文件中被配置成了依赖。

b. android

在构建文件中,占比最大的是android代码块,该代码块包含了全部Android特有配置,这些特有配置之所以可被使用,因为之前使用了Android插件。

  • compileSdkVersion:用来编译应用的Android API版本。
  • buildtoolsversion:构建工具和编译器使用的版本号。
  • defaultConfig:代码块用于应用的核心属性,此代码块属性可覆盖AndroidMainfest.xml文件中对应的条目。

applicationId是defaultConfig中的第一个属性,该属性覆盖了manifest文件中的packageName,但两者之间还是有差别:在Gradle被作为默认Android构建系统之前,AndroidMainfest.xml中的 package name 有两个用途,作为一个应用的唯一标识及在R资源类中被用作包名。

Gradle可轻易构建不同版本的应用,在之前使用variants构建不同版本时(例如一个免费版和付费版),不同版本需要有独立的标识符,才能在Google Play Store中以不同的应用出现,并且可以被同时安装。然而,资源代码和生成的R类,必须在任何时候都保持相同的包名,否则所有源文件都要随着构建的版本而改变。因此Android工具团队解耦了 package name 两种不同用法的原因。

定义在AndroidMainfest.xml文件中的package 继续用在源代码和R类中。之前被用作设备和Google Play唯一标识的package name ,现在被称为application id 。

minSdkVersiontargetSdkVersion 是defaultConfig中的第二、三个属性,前者属性被用来配置运行应用的最小API级别,后者用于通知系统该应用已在某特定Android版本通过测试,从而操作系统不必启用任何向前兼容的行为。(与之前的compileSdkVersion没有任何关系)

而后的versionCodeversionName 在AndroidMainfest.xml文件中的作用相同,即为应用定义一个版本号和一个人友好的版本名称,构建文件中的属性会全面覆盖AndroidMainfest.xml文件中的属性,因此在build.gradle中定义好后无需在mainfest文件再次定义。

buildType代码块可用来定义如何构建和打包不同构建类型的应用,后续讲解。

c. 依赖包

依赖代码块是标准Gradle配置的一部分(也就是为何要单独放在android代码块之外的原因),其定义了一个应用或依赖项目的所有依赖包。默认情况下,一个新的Android应用,在libs文件夹下对所有JAR文件构成依赖。


2. 任务

运行gradlew tasks任务,可知道一个项目中有哪些任务可被使用。

(1)基础任务

Gradle的Android插件使用了Java基础插件,而Java基础插件又使用了基础插件,基础插件添加了任务的标准生命周期和一些共同约定的属性,定义了assemnle 和 clean 任务,Java插件定义了check 和 buildtasks。在基础插件中,这些任务即不被实现,也不执行任何操作,只是被用来定义插件之间的约定,即添加实际工作的任务。

这些任务的约定如下:

  • assemble:集合项目的输出。
  • clean:清理项目的输出。
  • check:运行所有检查,通常是单元测试和集成测试。
  • build:同时运行assemble 和 check。

(2)Android任务

Android插件扩展了基本任务,并实现了它们的行为,下面是在Android环境下任务所做的事情:

  • assemble:为每个构建版本创建一个APK。
  • clean:删除所有的构建内容,例如APK文件。
  • check:运行Lint检查,若发现问题则可终止构建。
  • build:同时运行build 和 check。

assemble任务依赖于assembleDebug 和 assembleRelease,如果你添加了更多的构建类型,会有更多的任务。这意味着运行assemble将会触发每一个你所拥有的构建类型,并进行一次构建操作。


3.自定义构建

(1)Android Studio相关窗口

不必在枯燥的命令行界面运行Gradle任务,AS又一个专门包含了所有可用任务的工具窗Gradle:

这里写图片描述

在此窗口可以通过双击任务名称来运作某个任务,并且在Gradle Console工具窗中跟进任何正在执行任务的进度,如下图:(当然你也可以在Terminal工具窗中输入命令执行)

这里写图片描述

自定义构建的过程的方式有很多种,但是在AS中编写构建文件时,无论自定义什么,应该始终同步项目!特别是开始添加依赖或BuildConfig变量时,同步将变得尤其重要。

在编写settings.gradlebuild.gradle文件时,AS会提示你同步项目:Sync Project With Cradle Files。底层原理是运行了 generateDebugSources任务来生成所有必需的类。

这里写图片描述

(2)操纵manifest 条目

可以直接在构建文件而不是manifest文件中配置 applicationId、minSdkVersion、targetSdkVersion和versionName,除此之外,还有以下配置:

  • testApplicationId:针对instrument 测试APK的applicationId
  • testInstrumentationRunner:JUnit测试运行器的名称,被用来运行测试
  • signingConfig
  • proguardFile 和 proguardFiles

AS内部的Project Structure对话框可以修改基本的设置,对于每一个Android模版,可以修改Android插件的属性和所有manifest属性,在此做的任何更改都会写入到Gradle的构建配置文件。

(3)BuildConfig和资源

在SDK工具版本升级到17后,构建工具会生成一个 BuildConfig 类,该类包含一个按照构建类型设置值的 DEBUG 常量。如果一部分代码要在debugging 时期运行,例如常见的logging,此时DEBUG 会很有用。可以通过Gradle 扩展该文件,以便于在debug 和 release时可拥有不同的常量。

例如下例展示中的常量,可用于切换功能或设置服务器URL:(注意字符串值必须用转义双引号括起来才会生成实际意义上的字符串)

android {
    ......
    buildTypes {

        debug{
            buildConfigField("String", "API_URL","\"http://.......\"")
            buildConfigField("boolean", "LOG_HTTP_CALLS","true")
        }

        release{
            buildConfigField("String", "API_URL","\"http://.......\"")
            buildConfigField("boolean", "LOG_HTTP_CALLS","false")
        }

    }
}

(4)项目范围的设置

如果一个项目中有多个Android模块,比起人为修改每个模块的build.gradle构建文件,直接在项目的build.gradle构建文件设置应用到所有的模块,更加高效!前面已经讲过,allprojects代码块是如何定义依赖仓库,可使用相同策略运用Android 特定设置:

allprojects {
    apply plugin: 'com.android.application'

    android{
        compileSdkVersion 22
        buildToolsVersion "22.0.1"

    }
}



三. 依赖管理

依赖管理是Gradle 的优势之一,理想情况下在构建文件中添加一行代码,Gradle即可从远程仓库下载依赖,确保项目中可使用依赖中的类。

1. 依赖仓库

通常讨论的“依赖”指的是外部依赖,例如其他开发者提供的依赖库。若无Gradle管理工具,那么需要手动找到该依赖,下载JAR文件拷贝到项目引用,而且一般JAR文件名称中无版本号,会十分麻烦。

依赖仓库可以解决以上问题,它可以看作是文件的集合。Gralle默认情况下没有为项目定义任何依赖仓库,因此开发者需要在repositories 代码块中添加它们,若使用AS可以自动完成,代码块如下:

    repositories {
        jcenter()
    }

Gradle支持三种不同的依赖库:Maven、Ivy和静态文件或文件夹。在构建的执行阶段,依赖从依赖仓库中被取出来。(Gradle也有本地缓存,因此一个特定版本的依赖只会在计算机上下载一次)

一个依赖通常由三种元素定义:

  • group:指定了创建该依赖库的组织,通常是反向域名。
  • name:是依赖库的唯一标识。
  • version:指定了需要使用依赖库。

使用以上三个元素,就可以在dependencied代码块中声明一个依赖了:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'

    compile 'com.google.code.gson:gson:2.3'
}

下面是完整的Groovy映射标识:

compile group: 'com.google.code.gson:gson', name: 'son',version: '2.3'

(对于一个依赖而言,唯一需要的字段是name,为了表述清楚,建议添加group,version可确保依赖库不会自动更新,因为这可能导致构建失败)


2. 本地依赖

某些情况下仍需要手动下载JAR文件原生库,例如想创建自己的依赖库,这样可以在未发布于仓库时被多个项目复用。此种情况下,不能使用在线资源而是本地资源。

(1)文件依赖

可以使用Gradle 提供的files方法来添加JAR文件作为一个依赖,如下所示:

dependencies {
    compile files('libs/domoarigato.jar')
}

若有多个JAR文件,无需单个单个扩进来,直接一次添加一个完整的文件夹:

    compile fileTree('libs')

默认情况下新建的Android项目会有一个libs文件夹,会被声明为依赖使用的文件夹。一个过滤器可以保证只有JAR文件会被依赖,而不是简单地依赖文件夹中所有文件。

compile fileTree(dir: 'libs', include: ['*.jar'])

这意味着所有由Android Studio创建的Android 项目,都可以将JAR文件放置在libs文件见中,会自动包含在构建路径和最终的APK中。

(2)原生依赖库

用C或C++编写的依赖库可以背编译为特定平台的原生代码,这些依赖库通常包含几个.so 文件用于不同平台。Android插件默认支持原生依赖库,开发者需要做的就是创建一个 jniLibs文件夹,然后为每个平台创建子文件夹,将so 文件放到适当文件夹。

  • app
    • AndroidManifest.xml
    • jniLibs
      • armeabi
        • nativelib.so
      • armeabi-v7a
        • nativelib.so
      • mips
        • nativelib.so
      • x86
        • nativelib.so

(3)依赖项目

如果想要分享一个Android 资源的依赖库,需要创建一个依赖项目依赖项目通常和应用项目类似,不同之处在于输出:应用项目将生成一个可被安装和运行在Android设备上的APK,依赖项目则生成一个 .aar文件,可被Android应用项目用作依赖库。

创建和使用依赖项目模块

不同于应用Android应用插件,构建脚本需要应用Android依赖库插件:

apply plugin: 'com.android.library'

在应用包含依赖的方式有两种:

  • 在项目中当作一个模块。
  • 创建一个可在多个应用中复用的 .aar文件。

a. 如果在项目创建一个模块作为依赖项目,需要在setting.gradle 中添加钙模块,在应用模块中将它作为依赖:

Include ':app', ':library'

b. 在这种情况下依赖模块被称之为“依赖库”,并且文件夹名称与此相同,为了在Android模块中使用依赖库,需要在Android模块的 build.gradle 文件中添加一个依赖:

dependencies{
    compile project(':library')
}

使用 .aar文件

除了以上方法,还可以创建一个 .aar文件,然后将其作为一个依赖添加到你的项目中。在构建依赖库时,模块目录下的 build/output/aar 文件夹将会生成 .aar文件。为了添加一个.aar文件作为依赖,需要在应用模版中创建一个文件夹,复制 .aar文件至该文件夹,添加为依赖仓库:

repositories{
    flatDir{
        dirs 'arrs'
    }
}

这使得我们可以添加任何文件到该目录下,并将其作为依赖,并告知Gradle查找具有特定名称且扩展名为 .aar的依赖库。

dependencies{
    compile(name: 'libraryname', ext:'aar')
}

3. 依赖概念

了解几个依赖相关的概念,例如以下的配置概念解释了贯穿整个流畅作为依赖的 compile关键字。

(1)配置

有时需要使用特定设备上工作的SDK,例如特定厂商的蓝牙SDK,为了能够编译该代码,需要将SDK添加至编译类路径。不需要添加SDK到APK,因为已存在于设备中,这就是 依赖配置

Gradle将多个依赖添加至配置,并将其命名为 集文件,下面是一个Android应用或依赖库的标准配置:

  • compile
  • apk
  • provided
  • testCompile
  • androidTestCompile

compile是默认的配置,在编译主应用时包含所有的依赖,该配置不仅会将依赖添加至类路径,还会生成对应的APK。

若依赖使用了apk配置, 只会被打包到APK,不会添加到编译类路径,provided配置则相反。这两个配置只适用于JAR依赖,若在依赖项目中添加会报错。

testCompileandroidTestCompile配置会添加用于测试的额外依赖库,在运行测试相关的人物会被使用,特别是添加例如 JUnit 或 Espresso 测试框架时。(测试程序时使用这些框架就不会产生apk文件)

(2)语义化版本

版本化是依赖管理的重要部分,将依赖添加到JCenter等依赖仓库时,约定遵循了一套版本化规则,称之为语义化版本。版本数字的格式一般为 majorminorpatch,数字则按照下列规则依次增加:

  • 当做不兼容的API变化时,major版本增加。
  • 以向后兼容的方式添加功能时,minor版本增加。
  • 修复一些bug时,patch版本增加。(有点git的味道~)

(3)动态化版本

若希望在每次构建build应用或依赖库时,都能获取最新版本,可以使用动态化版本,例如以下:

dependencies{
    compile 'com.android.support:support-v4:22.2.+'
    compile 'com.android.support:appcompat-v7:22.2.+'
    compile 'com.android.support:recyclerview-v7:+'
}
  • 第一行设置Gradle 获取最新的patch 版本。
  • 第二行设置可获取每一个最新的 minor 版本,且minor版本至少为2。
  • 最后一行设置Gradle获取依赖库的最新版本。

在使用 动态化版本时需要格外注意,若允许Gradle获取最新版本,可当前版本并不稳定时测试版本,不建议使用。


4. Android Studio

(1)IDE添加依赖库

这里写图片描述

AS提供了添加依赖最简单的方式:打开AS的 Project Structure对话框,导航到Dependencies,获取当前依赖概要。不仅如此可直接在左下角添加新的依赖库,无须在 build.gradle文件中手动添加代码,直接通过IDE搜索JCenter。

(2)手动build.gradle文件添加代码

常规用法,以上方法有时无法搜索到心仪依赖库,更直接的在GitHub上找到对应依赖库,在其readme文档中获取到依赖库名称版本号。

这里写图片描述




四. 创建构建Variant

开发一个应用通常会有几个不同的版本,例如一个用于保证质量的测试版本和一个生产版本。不同的版本有不同的配置,例如测试API的URL。

在Gradle 中,有一些可扩展的概念可用来定义这些常见问题。每个由AS创建的新项目都会生成 debug 和 release构建类型,另一个概念是product flavor(不同定制的 产品),便于管理多个应用或依赖库版本。构建类型和product flavor经常结合使用,便于处理不同版本管理,称之为 构建variant

1. 构建类型

在Gradle的Android插件中,构建类型通常被用来定义如何构建一个应用或依赖库。每个构建类型都能特殊化,不论其中配置什么,可以在buildTypes 代码块中定义构建类型。下面是AS创建构建文件的标准buildTypes 代码块:

android{
    buildTypes { //构建类型
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
 }

新模块中默认 build.gradle文件配置了一个构建类型release,用于禁用清除无用资源(通过设置minifyEnabled 为false)和定义默认 ProGuard 配置文件的位置。开发人员可以更直观地在构建上使用 ProGuard。

在项目中 release构建类型不是唯一类型,默认情况下还有一个debug 类型,可包含至buildTypes 代码块,添加属性设置配置使调试更容易。

(1)创建构建类型

当默认设置不够用时,可以 自定义构建类型,只需在 buildTypes 代码块中新增一个对象即可,例如以下staging 自定义构建类型:

android{
    buildTypes { //构建类型
        staging.initWith(buildTypes.debug)
        staging {
            applicationIdSuffix ".staging"
            versionNameSuffix "-staging"
            debuggable = false
        }
    }
 }

staging 构建类型针对 applicationID定义了新的后缀,多了一个”.staging”,这意味着同一设备可同时安装 staging版本和 release版本。

staging.initWith() 方法创建了一个新的构建类型,并且复制了一个已存在的构建类型所有属性到新的构建类型中,可复写属性或定义额外的属性。

(2)源集

在创建一个新的构建类型时,Gradle 会创建一个新的源集,该目录名称默认和构建类型相同。此目录不是在定义新的构建类型时自动创建的,因此需要在构建类型使用自定义的源码和资源之前手动创建源集目录。

下面是标准的debug 和 release,另外一个额外 staging构建类型的目录结构:

这里写图片描述

使用源集,可以针对特定的构建类型覆写某些属性、添加自定义代码或布局字符串。注意:当添加资源例如一个类到构建类型时,可以耶添加到 debug 和 release源集,但不可添加至main源集,那样类会被定义两次,会出现异常。

(3)依赖

每个构建类型都可以有自己的依赖,Gradle自动为每个构建类型创建新的依赖配置,不同编译类型的项目依赖都可以不同,例如需要在staging和debug两个版本中使用不同的log框架,配置如下:

这里写图片描述


2. product flavors

之前谈论到情况是配置相同App或library的不同构建类型,即 代码编译成同一个程序的不同构建类型,若面临需求创建不同版本,即代码编译成不同的程序(包名也不同),例如免费版和收费版,此时涉及到 product flavors

android{
    productFlavors{
        red{
            applicationId 'com.gym.red'
            versionCode 1
        }
        blue{
            applicationId 'com.gym.blue'
            minSdkVersion 15
            versionCode 1
        }
    }
}

注意:product flavors和Build Type的属性不同,product flavors是ProductFlavors类中的对象,类似于构建文件中的defaultConfig对象,意味着product flavor 和defaultConfig 共享所有属性。


3. 签名配置

若需要讲应用发布到App Store,需要使用私钥签名,输入我们的keystore数据。如果是debug 版本,系统默认会帮我们配置这些信息。这些信息在gradle 中都配置在signingConfigs中。

这里写图片描述

配置之后我们需要在build type中直接使用:

这里写图片描述




五. 其他

1. 单元测试

为了确保应用或依赖库的质量,自动化测试是一个重要的方法,可以直接在AS或使用Gradle在命令行界面运行。AS和Gradle 的Android插件原生支持单元测试,但在使用之前需要做一些配置。

(1)JUnit

JUnit是一个受欢迎的单元测试依赖库,易编写测试且确保可读性。注意这些特定的单元测试只对业务逻辑有效,对Android SDK相关的代码无效。编写JU nit之前需要为测试用例创建一个文件夹,按照惯例命名为test,与main目录在同一级别,可以在src/test/java/com.gym.app 中创建测试类:

  • app
    • scr
      • main
        • java
          • com.gym.app
      • res
    • test
    • java
      • com.gym.app

为测试构建添加依赖确保使用JUnit,注意这里使用的是 testCompile 并非 Compile使用该配置可确保依赖只在运行时测试构建,不会在分发的app中打包。在常规的 assemble 任务中,通过 testCompile 添加的依赖将永远不会被包含在 release 的APK中。


2. Gradle 常用命令和参数

  • -v, –version (查看当前 Gradle 版本信息)
  • -h//(查看帮助信息)
  • -s, –stacktrace / -S, –full-stacktrace (如果发生异常则输出异常栈信息)
  • -i, –info / -d, –debug (控制日志级别)
  • –stop(停止正在运行的 Daemon)
  • -D, –system-prop / -P, –project-prop (传入特定参数)
  • -m, –dry-run / -x, –exclude-task (运行构建,但不执行任务的实际操作 )
  • –offline / –refresh-dependencies(离线模式构建,强制刷新依赖构建)
  • -b, –build-file / -c, –settings-file(指定 build 脚本,指定 settings 脚本 )
  • Ctrl + c (终止当前运行)
  • ./gradlew aFR > log.txt 2>&1 (输出所有内容到 log.txt)

3. 常用Task

Gradle 常用 Task

  • 缩写 ——只需提供足够的可以唯一区分出该任务的字符即可
    • ./gradlew assembleFullRelease
    • ./gradlew assFRel
    • ./gradlew aFR
  • tasks [–all](列出所有可运行的 task)
  • help –task (查看指定 task 的帮助信息)
  • androidDependencies (查看当前 project 的 android 依赖树)
  • dependencies –configuration (查看 configuration 的 依赖树)
  • clean (清除构建产出物 )
  • assemble, assemble{BuildVariant} (构建 apk,构建指定 Variant 的 ap)
  • install , install{BuildVariant} (构建 apk 并安装)
  • uploadArchives (构建 aar 并上传到指定依赖管理服务器)

Android 常用 Task

一次构建过程中,assembleRelease依赖了许多task,如下是一些比较关
键的task:

  • compileReleaseJavaWithJavac (编译Java文件)
  • mergeReleaseAssets (收集所有的assets)
  • mergeReleaseResources (收集所有的resources)
  • processReleaseManifest (生成最终的AndroidManif.xml文件)
  • transformClassesAndResourcesWithPro guardForRelease (混淆)
  • transformClassesWithDexForRelease (生成dex)
  • packageRelease (打包生成apk)

4. 官方学习url




声明:以上内容总结于《Gradle For Android》一书,主要详细归纳了前面几章的知识点,而后面部分使用较少,因此只着少许笔墨。其中需要强调的是Groovy语法这一块其实对于自定义任务比较重要,但是其内容较为固定,网上已有多篇博文总结,便不再赘述占用篇幅。此篇博文的重点还是在于Gradle与Android的关联使用,这涉及的知识点较多,较为零散,个人对其中某些概念总有些一知半解,因此以此篇博文记录学习,巩固其身。

后部分知识点不太常用,理解也较为晦涩,因此归纳地很大概,日后再做补充。

(吐槽:此书的翻译还是有些一言难尽,部分内容阅读起来着实晦涩了些,句子的分句有的不太合理,但还是感谢翻译大大,毕竟咱还没有直接啃英文书的能力~)


若有错误,虚心指教~

猜你喜欢

转载自blog.csdn.net/ITermeng/article/details/78579327