Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

配置编译版本

然后将它们打包成可供您测试、部署、签署和分发的 APK。

Android 编译系统会编译应用资源和源代码,然后将它们打包成可供您测试、部署、签署和分发的 APK。Android Studio 使用高级编译工具包 Gradle 来自动执行和管理编译流程,同时也允许您定义灵活的自定义编译配置。每个编译配置均可定义自己的一组代码和资源,同时重用所有应用版本共用的部分。Android Plugin for Gradle 与该编译工具包一起使用,共同提供专用于编译和测试 Android 应用的流程和可配置设置。

Gradle 和 Android 插件独立于 Android Studio 运行。这意味着,您可以在 Android Studio 内、计算机上的命令行或未安装 Android Studio 的计算机(如持续集成服务器)上编译 Android 应用。如果您不使用 Android Studio,可以学习如何从命令行编译和运行应用。无论您是从命令行、在远程计算机上还是使用 Android Studio 编译项目,编译版本的输出都相同。

注意:由于 Gradle 和 Android 插件独立于 Android Studio 运行,因此您需要单独更新编译工具。请阅读版本说明,了解如何更新 Gradle 和 Android 插件

Android 编译系统非常灵活,让您能够在不修改应用核心源文件的情况下进行自定义编译配置。本部分将介绍 Android 编译系统的工作原理,以及它如何帮助您对多个编译配置进行自定义和自动化处理。如果您只想详细了解如何部署应用,请参阅在 Android Studio 中编译和运行应用。要立即开始使用 Android Studio 创建自定义编译配置,请参阅配置编译变体

编译流程

编译流程涉及许多将项目转换成 Android 应用软件包 (APK) 的工具和流程。编译流程非常灵活,因此了解它的一些底层工作原理会很有帮助。

图 1. 典型 Android 应用模块的编译流程。

典型 Android 应用模块的编译流程(如图 1 所示)按照以下常规步骤执行:

  1. 编译器将您的源代码转换成 DEX 文件(Dalvik 可执行文件,其中包括在 Android 设备上运行的字节码),并将其他所有内容转换成编译的资源。
  2. APK 打包器将 DEX 文件和编译的资源组合成单个 APK。不过,必须先为 APK 签名,然后才能将应用安装并部署到 Android 设备上。
  3. APK 打包器使用调试或发布密钥库为 APK 签名:
    1. 如果您编译的是应用调试版本(即专用于测试和分析的应用),打包器会使用调试密钥库为应用签名。Android Studio 自动使用调试密钥库配置新项目。
    2. 如果您编译的是打算向外发布的应用发布版本,打包器会使用发布密钥库为应用签名。要创建发布密钥库,请参阅在 Android Studio 中为应用签名
  4. 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时占用的内存。

编译流程结束时,您将获得可用于部署和测试的调试 APK,或者可用于发布给外部用户的发布 APK。

自定义编译配置

Gradle 和 Android 插件可帮助您完成以下方面的编译配置:

编译版本类型
编译版本类型定义 Gradle 在编译和打包应用时使用的某些属性,通常针对开发生命周期的不同阶段进行配置。例如,调试编译版本类型支持调试选项,并使用调试密钥为 APK 签名;而发布编译版本类型可以压缩 APK、对 APK 进行混淆处理,以及使用发布密钥为 APK 签名以进行分发。您必须至少定义一个编译版本类型,才能编译应用。默认情况下,Android Studio 会创建调试和发布编译版本类型。要开始为应用自定义打包设置,请学习如何配置编译版本类型
正式版类型
正式版类型代表您可以向用户发布的不同版本的应用,如免费和付费版应用。您可以自定义正式版类型以使用不同的代码和资源,同时共享和重用所有应用版本共用的部分。正式版类型是可选的,并且您必须手动创建。要开始创建不同版本的应用,请学习如何配置正式版类型
编译变体
编译变体是版本类型与正式版类型的混合产物,也是 Gradle 用来编译应用的配置。使用编译变体,您可以在开发期间编译调试版本的正式版类型,或者编译已签名的发布版本的正式版类型以进行分发。虽然您无法直接配置编译变体,但可以配置组成它们的编译版本类型和正式版类型。创建其他编译版本类型或正式版类型也会创建其他编译变体。要了解如何创建和管理编译变体,请参阅配置编译变体概览。
清单条目
您可以在编译变体配置中为清单文件的某些属性指定值。这些编译值会替换清单文件中的现有值。如果您要为模块生成多个 APK,让每一个 APK 文件都具有不同的应用名称、最低 SDK 版本或目标 SDK 版本,便可运用这一技巧。存在多个清单时,Gradle 会合并清单设置
依赖项
编译系统会管理来自本地文件系统以及来自远程代码库的项目依赖项。这样一来,您就不必手动搜索、下载依赖项的二进制文件包以及将它们复制到项目目录内。要了解详情,请参阅添加编译依赖项
签名
编译系统让您能够在编译配置中指定签名设置,并且会在编译过程中自动为 APK 签名。编译系统通过已知凭据使用默认密钥和证书为调试版本签名,以避免在编译时提示输入密码。除非您明确定义发布版本的签名配置,否则编译系统不会为该版本签名。如果您没有发布密钥,可以按为应用签名中所述生成一个。
ProGuard
编译系统让您能够为每个编译变体指定不同的 ProGuard 规则文件。编译系统可在编译过程中运行 ProGuard,以对类进行压缩和混淆处理。
多 APK 支持
编译系统让您能够自动编译不同的 APK,并且每个 APK 只包含特定屏幕密度或应用二进制接口 (ABI) 所需的代码和资源。如需了解详情,请参阅编译多个 APK

编译配置文件

创建自定义编译配置需要您对一个或多个编译配置文件(即 build.gradle 文件)进行更改。这些纯文本文件使用领域特定语言 (DSL) 以 Groovy 描述和操作编译逻辑,其中 Groovy 是一种适用于 Java 虚拟机 (JVM) 的动态语言。您无需了解 Groovy 便可开始配置编译,因为 Android Plugin for Gradle 引入了您需要的大多数 DSL 元素。如需详细了解 Android 插件 DSL,请参阅 DSL 参考文档

开始新项目时,Android Studio 会自动为您创建其中的部分文件(如图 2 所示),并为其填充合理的默认值。

图 2. Android 应用模块的默认项目结构。

有一些 Gradle 编译配置文件是 Android 应用的标准项目结构的组成部分。您必须了解其中每个文件的范围和用途及其应定义的基本 DSL 元素,才能着手配置编译版本。

Gradle 设置文件

settings.gradle 文件位于项目根目录,用于指示 Gradle 在编译应用时应将哪些模块包含在内。对大多数项目而言,该文件很简单,只包含以下内容:

    include ‘:app’
    

不过,多模块项目需要指定应包含在最终编译版本之中的每个模块。

顶级编译文件

顶级 build.gradle 文件位于项目根目录,用于定义适用于项目中所有模块的编译配置。默认情况下,顶级编译文件使用 buildscript 代码块来定义项目中所有模块共用的代码库和依赖项。以下代码示例描述的默认设置和 DSL 元素可在新建项目后的顶级 build.gradle 文件中找到。

    /**
     * The buildscript block is where you configure the repositories and
     * dependencies for Gradle itself—meaning, you should not include dependencies
     * for your modules here. For example, this block includes the Android plugin for
     * Gradle as a dependency because it provides the additional instructions Gradle
     * needs to build Android app modules.
     */

    buildscript {

        /**
         * The repositories block configures the repositories Gradle uses to
         * search or download the dependencies. Gradle pre-configures support for remote
         * repositories such as JCenter, Maven Central, and Ivy. You can also use local
         * repositories or define your own remote repositories. The code below defines
         * JCenter as the repository Gradle should use to look for its dependencies.
         *
         * New projects created using Android Studio 3.0 and higher also include
         * Google's Maven repository.
         */

        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.4.2 as a classpath dependency.
         */

        dependencies {
            classpath 'com.android.tools.build:gradle:3.4.2'
        }
    }

    /**
     * The allprojects block is where you configure the repositories and
     * dependencies used by all modules in your project, such as third-party plugins
     * or libraries. However, you should configure module-specific dependencies in
     * each module-level build.gradle file. For new projects, Android Studio
     * includes JCenter and Google's Maven repository by default, but it does not
     * configure any dependencies (unless you select a template that requires some).
     */

    allprojects {
       repositories {
           google()
           jcenter()
       }
    }
    

配置项目范围的属性

对于包含多个模块的 Android 项目,在项目级别定义某些属性并在所有模块之间共享这些属性可能很有用。为此,您可以将额外属性添加到顶级 build.gradle 文件中的 ext 代码块。

    buildscript {...}

    allprojects {...}

    // This block encapsulates custom properties and makes them available to all
    // modules in the project.
    ext {
        // The following are only a few examples of the types of properties you can define.
        compileSdkVersion = 28
        // You can also create properties to specify versions for dependencies.
        // Having consistent versions between modules can avoid conflicts with behavior.
        supportLibVersion = "28.0.0"
        ...
    }
    ...
    

要从同一项目中的模块访问这些属性,请在该模块的 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 {
        implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
        ...
    }
    

注意:虽然 Gradle 可让您在模块级别定义项目范围的属性,但您应避免这样做,因为这样会导致共享这些属性的模块进行耦合。模块耦合使得以后将模块作为独立项目导出更加困难,并且实际上会妨碍 Gradle 利用并行项目执行来加快多模块编译。

模块级编译文件

模块级 build.gradle 文件位于每个 project/module/ 目录,用于为其所在的特定模块配置编译设置。您可以通过配置这些编译设置来提供自定义打包选项(如附加编译版本类型和正式版类型),以及替换 main/ 应用清单或顶级 build.gradle 文件中的设置。

以下 Android 应用模块 build.gradle 文件示例大体说明了您应该了解的一些基本 DSL 元素和设置。

    /**
     * The first line in the build configuration applies the Android plugin for
     * Gradle to this build and makes the android block available to specify
     * Android-specific build options.
     */

    apply plugin: 'com.android.application'

    /**
     * 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.
       */

      compileSdkVersion 28

      /**
       * 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.
       *
       * This property is optional because the plugin uses a recommended version of
       * the build tools by default.
       */

      buildToolsVersion "29.0.0"

      /**
       * 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.
       */

      defaultConfig {

        /**
         * applicationId uniquely identifies the package for publishing.
         * However, your source code should still reference the package name
         * defined by the package attribute in the main/AndroidManifest.xml file.
         */

        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 28

        // Defines the version number of your app.
        versionCode 1

        // Defines a user-friendly version name for your app.
        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.
       */

      buildTypes {

        /**
         * By default, Android Studio configures the release build type to enable code
         * shrinking, using minifyEnabled, and specifies the Proguard settings file.
         */

        release {
            minifyEnabled true // Enables code shrinking for the release build type.
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
      }

      /**
       * The productFlavors block is where you can configure multiple product flavors.
       * This allows you to create different versions of your app that can
       * override the defaultConfig block with their own settings. Product flavors
       * are optional, and the build system does not create them by default.
       *
       * This example creates a free and paid product flavor. Each product flavor
       * then specifies its own application ID, so that they can exist on the Google
       * Play Store, or an Android device, simultaneously.
       *
       * If you declare product flavors, you must also declare flavor dimensions
       * and assign each flavor to a flavor dimension.
       */

      flavorDimensions "tier"
      productFlavors {
        free {
          dimension "tier"
          applicationId 'com.example.myapp.free'
        }

        paid {
          dimension "tier"
          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"
        }
      }
    }

    /**
     * The dependencies block in the module-level build configuration file
     * specifies dependencies required to build only the module itself.
     * To learn more, go to Add build dependencies.
     */

    dependencies {
        implementation project(":lib")
        implementation 'com.android.support:appcompat-v7:28.0.0'
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    }
    

Gradle 属性文件

Gradle 还包含两个属性文件,位于项目根目录,可用于指定 Gradle 编译工具包本身的设置:

gradle.properties
您可以在其中配置项目范围的 Gradle 设置,如 Gradle 守护进程的最大堆大小。如需了解详情,请参阅编译环境
local.properties
配置编译系统的本地环境属性,如 SDK 安装路径。由于此文件的内容由 Android Studio 自动生成并且专用于本地开发者环境,因此您不应手动修改此文件或将其检入版本控制系统。

将项目与 Gradle 文件同步

当您在项目中对编译配置文件进行更改时,Android Studio 会要求您同步项目文件,以便其导入编译配置更改并执行一些检查来确保您的配置不会造成编译错误。

要同步项目文件,请点击做出更改后出现的通知栏中的 Sync Now(如图 3 所示),或者点击菜单栏中的 Sync Project 图标 。如果 Android Studio 发现您的配置有任何错误,例如:您的源代码使用了只有在 compileSdkVersion 以上的 API 级别中才会提供的 API 功能,则会显示 Messages 窗口来说明该问题。

图 3. 在 Android Studio 中将项目与编译配置文件同步。

源集

Android Studio 按逻辑关系将每个模块的源代码和资源分组为源集。模块的 main/ 源集包含其所有编译变体共用的代码和资源。其他源集目录是可选的,在您配置新的编译变体时,Android Studio 不会自动为您创建这些目录。不过,创建类似于 main/ 的源集有助于让 Gradle 只有在编译特定应用版本时才应使用的文件和资源井然有序:

src/main/
此源集包含所有编译变体共用的代码和资源。
src/buildType/
创建此源集可添加特定版本类型专用的代码和资源。
src/productFlavor/
创建此源集可添加特定正式版类型专用的代码和资源。

注意:如果将编译版本配置为组合多个正式版类型,则可为类型维度间正式版类型的各个组合创建源集目录:src/productFlavor1ProductFlavor2/

src/productFlavorBuildType/
创建此源集可添加特定编译变体专用的代码和资源。

例如,要生成应用的“fullDebug”版本,编译系统需要合并来自以下源集的代码、设置和资源:

  • src/fullDebug/(编译变体源集)
  • src/debug/(编译版本类型源集)
  • src/full/(正式版类型源集)
  • src/main/(主源集)

注意:当您在 Android Studio 中使用 File > New 菜单选项新建文件或目录时,可以针对特定源集进行创建。可供您选择的源集取决于您的编译配置,如果所需目录尚不存在,Android Studio 会自动创建。

如果不同源集包含同一文件的不同版本,Gradle 将按以下优先顺序决定使用哪一个文件(左侧源集会替换右侧源集的文件和设置):

编译变体 > 编译版本类型 > 正式版类型 > 主源集 > 库依赖项

这样一来,Gradle 便可使用专用于您试图编译的编译变体的文件,同时重用与其他应用版本共用的 Activity、应用逻辑和资源。在合并多个清单时,Gradle 使用同一优先级顺序,这样每个编译变体都能在最终清单中定义不同的组件或权限。要详细了解如何创建自定义源集,请转到创建编译变体的源集