十一、构建

在这一章中,我们将讨论应用的构建过程。尽管使用终端和 Android Studio IDE 的图形界面都可以构建带有源文件的应用,但这不是 Android Studio 的介绍,也不是代码参考。对于这种类型的深入指导,请参考包括的帮助或其他书籍和在线资源。

我们在这一章要做的是看看与构建相关的概念和方法,让构建过程适应你的需要。

与构建相关的文件

在 Android Studio 中创建新项目后,您将看到以下与构建相关的文件:

  • build.gradle

    这是与项目相关的顶级构建文件。它包含项目包含的所有模块共有的存储库和依赖项的声明。对于简单的应用,通常不需要编辑这个文件。

  • gradle.properties

    这包含与 Gradle 构建相关的技术设置。通常不需要编辑这个文件。

  • gradlew 和 gradlew.bat

    这些是包装器脚本,因此您可以使用终端而不是 Android Studio IDE 来运行构建。

  • local.properties

    它保存了与您的 Android Studio 安装相关的生成的技术属性。您不应编辑此文件。

  • 设置.等级

    这将告诉您哪些模块是项目的一部分。如果你添加了新的模块,Android Studio 会处理这个文件。

  • app/build.gradle

    这是一个与模块相关的构建文件。这是配置模块的重要依赖项和构建过程的地方。Android Studio 将为您创建第一个名为app的模块,包括相应的构建文件,但将app作为名称只是一种约定。额外的模块会有你随意选择的不同名字,它们都有自己的构建文件。如果你愿意,甚至可以将app改名为一个更适合你需要的名字。

模块配置

项目的每个模块都包含自己的构建文件build.gradle。如果您让 Android Studio 为您创建一个新项目或模块,它也会为您创建一个初始构建文件。具有 Kotlin 支持的模块的基本构建文件如下所示(忽略¬和后面的换行符):

apply plugin: "com.android.application"
apply plugin: "kotlin-android"
apply plugin: "kotlin-android-extensions"

android {
  compileSdkVersion 26
  defaultConfig {
      applicationId "de.pspaeth.xyz"
      minSdkVersion 16
      targetSdkVersion 26
      versionCode 1
      versionName "1.0"
      testInstrumentationRunner ¬
        "android.support.test.runner.AndroidJUnitRunner"
  }
  buildTypes {
      release {
          minifyEnabled false
          proguardFiles ¬
            getDefaultProguardFile( ¬
              "proguard-android.txt"), ¬
            "proguard-rules.pro"
      }
  }
}

dependencies {
  implementation fileTree(dir: 'libs', ¬
      include: ['*.jar'])
  implementation ¬
    "org.jetbrains.kotlin:kotlin-stdlib-jre7: ¬
          $kotlin_version"
  implementation ¬
    "com.android.support:appcompat-v7:26.1.0"
  implementation ¬
    "com.android.support.constraint: ¬
          constraint-layout:1.0.2"
  testImplementation "junit:junit:4.12"
  androidTestImplementation ¬
    "com.android.support.test:runner:1.0.1"
  androidTestImplementation ¬
    "com.android.support.test.espresso: ¬
          espresso-core:3.0.1"
}

注意 Gradle 中的""字符串可以包含${}占位符,而’ ’字符串则不能。除此之外,它们是可以互换的。

其内容如下:

  • apply plugin:行加载并应用 Android 和 Kotlin 开发所需的 Gradle 插件。

  • 元素指定了 Android 插件的设置。

  • 元素描述了模块的依赖关系。implementation关键字意味着编译模块和运行模块都需要依赖关系。后者意味着依赖关系包含在 APK 文件中。像xyzImplementation这样的标识符指的是构建类型或者源集xyz。您可以看到,对于位于src/test的单元测试,添加了 jUnit 库,而对于src/androidTest,测试运行器和espresso都被使用。如果您更喜欢构建类型或产品风格,您可以用构建类型名称或产品风格名称替换xyz。如果您想要引用一个 variant,它是一个构建类型和产品风格的组合,您还必须在一个configurations { }元素中声明它。这里有一个例子:

    ```kt configurations { // flavor = "free", type = "debug" freeDebugCompile {} }

    ```

  • 关于defaultConfig { }buildTypes { },请参见以下章节。

dependencies {...}部分中的其他关键字包括:

  • 实施

    我们讨论过这个。它表明编译和运行应用都需要依赖关系。

  • API

    这与implementation相同,但除此之外,它还允许依赖关系泄漏到应用的客户端。

  • 编译

    这是api的旧别名。不要用。

  • 完全地

    编译需要依赖项,但不会包含在应用中。对于像源代码预处理程序之类的纯源代码库来说,这种情况经常发生。

  • 运行时仅

    编译时不需要依赖项,但会包含在应用中。

模块通用配置

模块的build.gradle文件中的defaultConfig { ... }元素指定了构建的配置设置,与所选择的变量无关(见下一节)。可以在 Android Gradle DSL 参考中查找可能的设置,但常见的设置如下所示:

defaultConfig {

  // Uniquely identifies the package for publishing.
  applicationId 'com.example.myapp'

  // The minimum API level required to run the app.
  minSdkVersion 24

  // The API level used to test the app.
  targetSdkVersion 26

  // The version number of your app.
  versionCode 42

  // A user-friendly version name for your app.
  versionName "2.5"
}

模块构建变体

构建变体对应于构建过程生成的不同的.apk文件。构建变体的数量由以下公式给出:

Number of Build Variants =
     (Number of Build Types) x (Number of Product Flavors)

在 Android Studio 中,你可以通过构建➤在菜单中选择构建变体来选择构建变体。在接下来的部分中,我们将描述什么是构建类型和产品风格。

构建类型

构建类型对应于应用开发的不同阶段。如果你开始一个项目,Android Studio 会为你设置两种构建类型:开发发布。如果你打开模块的build.gradle文件,你可以看到里面的android { ... }(忽略¬,包括下面的换行符)。

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

尽管你在这里看不到一个debug类型,但它确实存在。它没有出现的事实仅仅意味着debug类型使用它的默认设置。如果您需要更改默认值,只需添加一个debug部分,如下所示:

buildTypes {
   release {
     ...
   }
   debug {
     ...
   }
}

您并不局限于使用预定义的构建类型之一。您可以定义其他构建类型,例如:

buildTypes {
   release {
     ...
   }
   debug {
     ...
   }
   integration {
     initWith debug
     manifestPlaceholders = ¬
            [hostName:"internal.mycompany.com"]
     applicationIdSuffix ".integration"
   }
}

这定义了一个名为integration的新构建类型,它通过initWithdebug继承而来,另外还添加了一个定制的 app 文件后缀,并提供了一个占位符用于清单文件。您可以在那里指定的设置相当多。如果你在你最喜欢的搜索引擎中输入 android gradle 插件 dsl 参考,你就可以找到它们。

另一个我们还没有谈到的标识符是proguardFiles标识符。一个用于过滤和/或模糊文件,这些文件将在分发应用之前包含在应用中。如果你用它来过滤,请首先权衡利弊。有了现代设备,现在省几兆也起不了多大作用。如果你想用它来混淆,注意如果反射被你的代码或者被引用的库使用,这可能会带来麻烦。而且模糊处理并不能真正防止劫机者在反编译后使用你的代码;只会让事情变得更难。所以,仔细考虑一下使用proguard的好处。如果您认为它符合您的需要,您可以在在线文档中找到有关如何使用它的详细信息。

产品风味

产品风格允许区分不同的功能集或不同的设备需求,但您可以在最适合自己的地方进行区分。

默认情况下,Android Studio 不会为一个新项目或模块准备不同的产品风格。如果您需要它们,您必须在文件build.gradleandroid { ... }元素中添加一个productFlavors { ... }部分。这里有一个例子:

buildTypes {...}
flavorDimensions "monetary"
productFlavors {
    free {
        dimension "monetary"
        applicationIdSuffix ".free"
        versionNameSuffix "-free"
    }
    paid {
        dimension "monetary"
        applicationIdSuffix ".paid"
        versionNameSuffix "-paid"
    }
}

在这里,你可以看看 Android Gradle DSL 参考中可能的设置。这将导致以下形式的 apk:

app-free-debug.apk
app-paid-debug.apk
app-free-release.apk
app-paid-release.apk

你甚至可以扩展维度。如果在flavorDimensions行中加入更多元素,比如flavorDimensions ”monetary”, ”apilevel”,就可以加入更多的味道。

flavorDimensions "monetary", "apilevel"
productFlavors {
    free {
        dimension "monetary" ... }
    paid {
        dimension "monetary" ... }
    sinceapi21 {
        dimension "apilevel"
        versionNameSuffix "-api21" ... }
    sinceapi24 {
        dimension "apilevel"
        versionNameSuffix "-api24" ... }
}

这最终将为您提供以下一组 APK 文件:

app-free-api21-debug.apk
app-paid-api21-debug.apk
app-free-api21-release.apk
app-paid-api21-release.apk
app-free-api24-debug.apk
app-paid-api24-debug.apk
app-free-api24-release.apk
app-paid-api24-release.apk

为了过滤掉某些可能的变体,在构建文件中添加一个variantFilter元素,并编写以下代码:

variantFilter { variant ->
  def names = variant.flavors*.name  // this is an array
  // To filter out variants, make a check here and then
  // do a "setIgnore(true)" if you don't need a variant.
  // This is just an example:
  if (names.contains("sinceapi24") &&
        names.contains("free")) {
    setIgnore(true)
  }
}

源集

如果在 Android Studio 中创建一个项目,切换到项目视图,可以看到在src文件夹里面有一个main文件夹。这对应于main源集,它是默认配置和使用的单个默认源集。见图 11-1

img/463716_1_En_11_Fig1_HTML.jpg

图 11-1

主源集

您可以有更多的集合,它们对应于构建类型、产品风格和构建变体。一旦您添加了更多的源集,一个构建将导致合并当前的构建变体、它所包含的构建类型、它所包含的产品风格,最后是主源集。要查看构建中将包含哪些源集,请打开窗口右侧的 Gradle 视图,并运行sourceSets任务。这将产生一个很长的列表,您可以看到如下条目:

main
Java sources: [app/src/main/java]

debug
Java sources: [app/src/debug/java]

free
Java sources: [app/src/free/java]

freeSinceapi21
Java sources: [app/src/freeSinceapi21/java]

freeSinceapi21Debug
Java sources: [app/src/freeSinceapi21Debug/java]

freeSinceapi21Release
Java sources: [app/src/freeSinceapi21Release/java]

paid
Java sources: [app/src/paid/java]

paidSinceapi21
Java sources: [app/src/paidSinceapi21/java]

release
Java sources: [app/src/release/java]

sinceapi21
Java sources: [app/src/sinceapi21/java]

这将告诉您,如果您选择一个名为freeSinceapi21Debug的构建变体,构建过程将在这些文件夹中查找类:

app/src/freeSinceapi21Debug/java
app/src/freeSinceapi21/java
app/src/free/java
app/src/sinceapi21/java
app/src/debug/java
app/src/main/java

同样,它会在相应的文件夹中查找资源、素材和AndroidManifest.xml文件。虽然 Java 或 Kotlin 类不能在这样的构建链中重复,但是清单文件以及资源和素材文件将被构建过程合并。

在文件build.gradledependencies { ... }部分中,您可以根据构建变体分派依赖项。只需在任何设置前添加一个骆驼大小写版本的源集。例如,如果对于freeSinceapi21变体,您想要包含一个:mylib的编译依赖项,请编写以下代码:

freeSinceapi21Compile ':mylib'

从控制台运行构建

你不必使用 Android Studio 来构建应用。虽然使用 Android Studio 引导应用项目是一个好主意,但在此之后,您可以使用终端构建应用。这就是 Gradle 包装脚本gradlewgradlew.bat的用途。第一个是 Linux 的,第二个是 Windows 的。在下面的段落中,我们将看一下 Linux 的一些命令行命令;如果你有 Windows 开发机器,就用BAT脚本代替。

在前面的章节中,我们已经看到了每个构建的基本构建块由一个或多个在构建期间执行的任务组成。所以,我们首先想知道哪些任务实际存在。为此,要列出所有可用的任务,请输入以下内容:

./gradlew tasks

这将为您提供一个详细的列表和每个任务的一些描述。在接下来的几节中,我们将看看其中的一些任务。

要为构建类型debugrelease构建应用 APK 文件,请输入以下内容之一:

./gradlew assembleDebug
./gradlew assembleRelease

这将在<PROJECT>/<MODULE>/build/outputs中创建一个 APK 文件。当然,您也可以指定您在build.gradle中定义的任何定制构建类型。

要构建调试类型 APK,然后将其安装在连接的设备或仿真器上,请输入以下内容:

./gradlew installDebug

这里,对于参数中的Debug部分,您可以使用变量的大小写名称替换任何构建变量。这将在连接的设备上安装应用。但是,它不会自动运行它;你必须手动操作!要安装运行 app,请参见第 18 章

如果您想找出您的应用的任何模块有哪些依赖关系,请查看依赖关系树,并输入以下 or 并用app替换有问题的模块名称:

./gradlew dependencies :app:dependencies

这提供了一个相当长的清单,所以您可能希望将它通过管道传输到一个文件中,然后在编辑器中研究结果。

./gradlew dependencies :app:dependencies > deps.txt

签署

每个应用的 APK 文件都需要签名,然后才能在设备上运行。对于debug构建类型,会自动为你选择一个合适的签名配置,所以对于调试开发阶段,你不需要关心签名。

然而,一个发布的 APK 需要一个正确的签名配置。如果您使用 Android Studio 的构建➤生成签名的 APK 菜单项,Android Studio 将帮助您创建和/或使用适当的密钥。但是您也可以在模块的build.gradle文件中指定签名配置。为此,添加一个signingConfigs { ... }部分,如下所示:

android {
  ...
  defaultConfig {...}
  signingConfigs {
      release {
          storeFile file("myrelease.keystore")
           storePassword "passwd"
          keyAlias "MyReleaseKey"
          keyPassword "passwd"
      }
  }
  buildTypes {
      release {
          ...
          signingConfig signingConfigs.release
      }
  }
}

此外,从发布构建类型内部,引用列表中的signingConfig所示的签名配置。您需要提供的密钥库是一个标准的 Java 密钥库;请参阅 Java 的在线文档,了解如何构建一个。或者,您可以让 Android Studio 帮助您创建一个密钥库,使用在菜单中选择构建➤生成签名的 APK 时弹出的对话框。