Android build system:构建系统的组成及其原理

  • Android build system
    • 组成部分
    • Gradle 、Gradle插件和Build Tools的下载安装
    • Android Studio、Gradle、Gradle插件、Build Tools它们的版本关系
  • Gradle 的各种构建配置文件
    • The Gradle Settings File
    • Build File
      • 项目的build.gradle文件
      • 模块的build.gradle文件
      • Source sets 及 app 构建原理
    • Gradle properties files
    • gradle-wrapper.properties(gradle版本统一管理文件)

Android build system


组成部分

Android build system 的组成部分:Gradle + Android plugin for Gradle

android app打包流程(即构建流程):
Android build system:构建系统的组成及其原理_第1张图片

  • Gradle:用于构建项目,即设置app打包过程的各种配置,如使用哪些SourceCode、哪些依赖、是否打签名包等。

  • Gradle 插件:使系统能支持运行Gradle。与Gradle 是独立运行的,因此需要独自下载。

  • Build Tools:Android 构建的相关工具都在这里面,位于./sdk/build-tools/目录下,它提供了类似aapt、dx这样的工具,gradle则是使用这样的工具来完成相应的构建任务。

Gradle和Gradle插件是分开的,那它们各自是如何下载的呢?

Gradle 、Gradle插件和Build Tools的下载安装

1、 Gradle
打开一个项目时,AS会根据项目中gradle > wrapper > gradle-wrapper.properties 文件中的distributionUrl=https://services.gradle.org/distributions/gradle-2.4-all.zip 设置的Gradle版本去查找。

那么AS是在什么地方查找的呢?
通过在Android Studio中依次点击File > Settings > Build, Execution, Deployment > Gradle,我们可以锁定当前项目使用的Gradle的位置。
- 若选中Use default gradle wrapper(recommended),则设置的Gradle位置为Service directory path中的路径;
- 若选中Use local gradle distribution,则设置的Gradle位置为Gradle home中的路径。
注:Service directory path是全局级的,Use default gradle wrapper(recommended)与Use local gradle distribution是项目级的,优先级高于全局级的设置。
Android build system:构建系统的组成及其原理_第2张图片
通常我们都是选择Use default gradle wrapper(recommended)。
选择Use default gradle wrapper(recommended) 之后,AS根据gradle-wrapper.properties 文件中的配置去service directory path下查找(后面介绍gradle-wrapper.properties会讲到),若没有则通过distributionUrl 去下载Gradle。

AS自动下载Gradle会很慢,两种解决办法
1. 修改gradle-wrapper.properties 中的Gradle版本:在打开项目前,修改gradle-wrapper.properties 中distributionUrl 的值为service directory path目录下已有的Gradle版本。打开项目后,有可能还要更改Gradle插件、build tools等的版本,详情看 “Android Studio、Gradle、Gradle插件、build tools它们的版本关系”。
2. 手动下载Gradle:参考[Android Studio系列(五)] Android Studio手动配置Gradle的方法(windows /Linux适用)

2、 Gradle 插件
都是在项目的build.gradle 文件(The Top-level Build File)中设置依赖,自动下载的,不需要fq:

buildscript {
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.0'//Gradle插件版本
    }
}

只要是能通过设置依赖获得的基本都不用fq,因为国内网能正常访问到在repository中设置的jCenter()和maven()等。

3、 Build Tools
在SDK Manager 中下载各个版本到本地,然后在模块的build.gradle文件(The Module-level Build File)中设置:

android {
    buildToolsVersion "27.0.3"
}

Android Studio、Gradle、Gradle插件、Build Tools它们的版本关系

关系:AS版本决定Gradle插件版本,Gradle插件版本决定Gradle版本(因为Gradle的运行必须由Gradle插件支持)和Build Tools 版本。

安装AS时,会自动安装相应版本的Gradle插件和Gradle,但可以根据项目需求更改到更高的版本。Gradle插件版本能否更改为低版本的还不知道,需要的话自己去试试。

Plugin version Required Gradle version
1.0.0 - 1.1.3 2.2.1 - 2.3
1.2.0 - 1.3.1 2.2.1 - 2.9
1.5.0 2.2.1 - 2.13
2.0.0 - 2.1.2 2.10 - 2.13
2.1.3 - 2.2.3 2.14.1+
2.3.0+ 3.3+
3.0.0+ 4.1+
3.1.0+ 4.4+

至于其他关系官网没有列出来表格,但对每个版本有单独介绍,网上有别人整理了一个,但不是最新的:
Android build system:构建系统的组成及其原理_第3张图片
注:AS版本号和Gradle插件版本号通常一样或接近。

最新版本关系参考官方文档:Android Plugin for Gradle release notes

Gradle 的各种构建配置文件


Gradle 的构建配置文件有如下几种:
Android build system:构建系统的组成及其原理_第4张图片

真实环境下的Gradle构建配置文件如下:
Android build system:构建系统的组成及其原理_第5张图片

The Gradle Settings File

告诉Gradle在打包app时应该包含哪些模块。

include ‘:app’//app是模块名称。

Build File

  • 类型:The Top-level Build File(项目的build.gradle文件)、The Module-level Build File(模块的build.gradle文件)

  • 与AndroidManifest.xml 的关系:
    可以在build.gradle 中设置manifest文件的某些属性,build.gradle 设置的属性会覆盖manifest文件中的属性。

项目的build.gradle文件

//buildscript 主要用于为Gradle配置仓库和依赖,不能为模块配置。
buildscript {

    //仓库中存放各种可依赖项目。
    //仓库可以是远程仓库如JCenter, Maven Central, and Ivy,也可以是自定义的远程仓库或本地仓库。
    repositories {
        google()
        jcenter()
    }

    /**
     * The dependencies block configures the dependencies Gradle needs to use
     * to build your project. The following line adds Android plugin for Gradle
     * version 3.1.0 as a classpath dependency.
     */
    //指定导入上面仓库的某个项目。
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.0'//Gradle插件版本
    }
}

//为所有模块提供仓库和依赖
allprojects {
   repositories {
       google()
       jcenter()
   }
}

//为所有模块定义公共变量
ext {
    // The following are only a few examples of the types of properties you can define.
    compileSdkVersion = 26
    // You can also create properties to specify versions for dependencies.
    // Having consistent versions between modules can avoid conflicts with behavior.
    supportLibVersion = "27.1.1"
    ...
}
...

公共变量在模块的build.gradle文件中的使用:

android {
  // Use the following syntax to access properties you defined at the project level:
  // rootProject.ext.property_name
  compileSdkVersion rootProject.ext.compileSdkVersion
  ...
}
...
dependencies {
    compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    ...
}

模块的build.gradle文件


//将Gradle 插件应用到模块中。
apply plugin: 'com.android.application'//主模块的设置,library模块的设置是’com.android.library’

/**
 * The android block is where you configure all your Android-specific
 * build options.
 */
android {

  /**
   * compileSdkVersion specifies the Android API level Gradle should use to
   * compile your app. This means your app can use the API features included in
   * this API level and lower.
   */

  //决定编译时使用哪个SDK版本的API。
  //当用户手机的系统版本高于compileSdkVersion时,高版本拥有的新特性无法使用。
  //即在开发时能使用的API只能属于compileSdkVersion或更低版本的,
  //更高版本的SDK中的API无法使用。
  compileSdkVersion 26

  /**
   * buildToolsVersion specifies the version of the SDK build tools, command-line
   * utilities, and compiler that Gradle should use to build your app. You need to
   * download the build tools using the SDK Manager.
   *
   * If you're using Android plugin 3.0.0 or higher, this property is optional—
   * the plugin uses a recommended version of the build tools by default.
   */

  buildToolsVersion "27.0.3"

  /**
   * The defaultConfig block encapsulates default settings and entries for all
   * build variants, and can override some attributes in main/AndroidManifest.xml
   * dynamically from the build system. You can configure product flavors to override
   * these values for different versions of your app.
   */
  //为所有build variants提供默认配置,会自动覆盖AndroidManifest.xml中的属性
  defaultConfig {
    //applicationId 必须是独一无二的。
    //编译后同级别目录下的manifest文件中的package name的值会替换为applicationId的值,用于应用商店识别是否是同个app。
    //一般applicationId 和package name设置成一样的值。
    applicationId 'com.example.myapp'

    // Defines the minimum API level required to run the app.
    minSdkVersion 15

    // Specifies the API level used to test the app.
    targetSdkVersion 26//只是用来指定测试app的API版本或者说手机系统版本

    versionCode 1

    versionName "1.0"
  }

  /**
   * The buildTypes block is where you can configure multiple build types.
   * By default, the build system defines two build types: debug and release. The
   * debug build type is not explicitly shown in the default build configuration,
   * but it includes debugging tools and is signed with the debug key. The release
   * build type applies Proguard settings and is not signed by default.
   */
  //默认提供debug 和release,可自定义更多的。
  //为debug 或release 版本的app设置构建配置。
  //如:为debug版本的app设置允许输出log,去除代码抖动、代码混淆、签名等不必要构建配置,提高打包速度。
  buildTypes {

    release {
        minifyEnabled true // Enables code shrinking for the release build type.
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//设置代码混淆文件。
    }
  }

  /**
   * If you're using Android plugin 3.0.0 or higher, you need to also declare
   * and assign each flavor to a flavor dimension. To learn more, read the
   * migration guide.
   */
  //系统不默认提供,需自己定义。用于据不同风格打不同版本的包,如多渠道打包。
  //多渠道打包:为每个应用商店提供独有的flavor 并进行相应配置。
  //与build type 不同之处:可以配置的属性不同,如可配置defaultConfig 中的属性。
  //会覆盖defaultConfig 中属性。每个flavor有自己独有的application ID
  productFlavors {
    free {
      applicationId 'com.example.myapp.free'
    }

    paid {
      applicationId 'com.example.myapp.paid'
    }
  }

  /**
   * The splits block is where you can configure different APK builds that
   * each contain only code and resources for a supported screen density or
   * ABI. You'll also need to configure your build so that each APK has a
   * different versionCode.
   */

  splits {
    // Settings to build multiple APKs based on screen density.
    density {

      // Enable or disable building multiple APKs.
      enable false

      // Exclude these densities when building multiple APKs.
      exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
    }
  }
}

/**
 * If you're using Android plugin 3.0.0 or higher, you should
 * use the new dependency configurations, which help you improve build speeds by
 * restricting which dependencies leak their APIs to other modules.
 */

//只属于该模块的依赖
dependencies {
    compile project(":lib")//依赖项目中其它模块
    compile 'com.android.support:appcompat-v7:27.1.1'
    compile fileTree(dir: 'libs', include: ['*.jar'])//依赖模块的libs文件夹中的所有jar文件
}

Source sets 及 app 构建原理

1、Source sets 概念
将模块中的java代码和资源分组。默认只有(主源集)main source set。
开发者可为每个build variants 提供各自的source set,每个source set下都有自己的manifest文件,可在android{}中设置。

build variants:构建变体,是打包app的渠道,每个build variants能打包得到一个app。这里的build variants指由一个build type 和一个flavor 组合而成的。单个的build type 或flavor 也可以称为build variants。

增加名为debug 的source set后的目录结构如下图:
Android build system:构建系统的组成及其原理_第6张图片

2、app 构建原理
打某个build variants 类型的包时主模块会将 相应 build variants 的source setmain source setlibrary dependence 融合,包括java代码、资源、manifest文件等的融合。(注:其他library 模块的融合也是如此。其实,主模块的融合过程就是整个app的构建过程。)

  • 融合规则
    若融合过程出现同名文件或属性时,优先级高的覆盖优先级低的。 优先级别:build variants(最高) > build type > flavor > main source set(主源集) > library dependence

    1. Java代码:所有java/ 目录下的代码被一起编译。这些合并到一起的代码的java类不能“同路径同名”(“同路径同名”指两个同名类在各自 source set 的 java/ 目录之后的路径相同)。
    2. 资源:不同source set 或library 中的资源名称可以一样。
    3. manifest文件:不同manifest文件中的配置可以一样。
      manifest文件的融合过程如下图:
      Android build system:构建系统的组成及其原理_第7张图片

    注意:一般优先级低的 manifest 文件设置的依赖、Build Tools等的版本不能高于优先级高的manifest文件,因为高版本具有向下兼容的功能。
    参考官方文档:Merge multiple manifest files

  • 总结:
    main source set 和其它source set中的java类不能“同路径同名”(“同路径同名”指两个同名类在java/ 目录之后的路径相同),但 resource资源 和 manifest文件中的文件或属性 可以一样(优先级高的覆盖优先级低的)。
    同一个 source set 中的 resource 资源 不能重复(因为同一个 source set 中它们的优先级一样)且只能有一个manifest文件。
    source set 的融合参考官方文档:Configure build variants

关于模块的build.gradle 更详细介绍参考:

  • 大全:Gradle 完整指南(Android)- 简书
    Android Gradle 配置选项合集
    Gradle配置最佳实践
  • 打包:Android 使用gradle打包的各种配置- 简书

Gradle properties files

  • gradle.properties
    This is where you can configure project-wide Gradle settings, such as the Gradle daemon’s maximum heap size.

  • local.properties
    Configures local environment properties for the build system, such as the path to the SDK installation. Because the content of this file is automatically generated by Android Studio and is specific to the local developer environment, you should not modify this file manually or check it into your version control system.

gradle-wrapper.properties(gradle版本统一管理文件)

文件内容:

#Mon Sep 21 12:15:49 CST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip

gradle-wrapper的作用就是使用统一的方式来管理gradle,保证gradle使用的是统一的版本。说明几点:

android studio首先从distributionBase/distributionPath查找gradle。
然后,从zipStoreBase/zipStorePath查找gradle。
如果上述都没有找到合适的gradle,则从distributionUrl指定的url去下载gradle。
注意:Linux中,这里需要在.bashrc中增加GRADLE_USER_HOME的变量定义。

导入github上的项目时,可以先更改项目中gradle文件夹下的gradle-wrapper.properties 中distributionUrl 的值为已有的gradle版本,然后再用AS打开项目,这样就不用到网上下载。

参考:
- 框架:Android Studio 之 Gradle 安装配置和编译


参考官方文档:
Configure your build

你可能感兴趣的:(Android build system:构建系统的组成及其原理)