Gradle for Android 第二章:自定义构建

来源:互联网 发布:西安哪里可以学php 编辑:程序博客网 时间:2024/06/05 03:15

  上一章我们已经介绍了Gradle的使用,创建和迁移Android项目。现在应该深入了解一下构建文件和一些有用的task了,看看Gradle和Android插件可以做些什么。
  在这一章,我们将介绍以下几点:

  • 了解Gradle文件
  • 初始构建task
  • 自定义构建

了解Gradle文件

  使用Android Studio创建了一个新项目之后,会生成三个Gradle文件,一个settings.gradle和一个build.gralde在项目根目录,另一个builde.gradle在Android应用模块中。以下便于是项目的Gradle文件结构:

MyApp├── build.gradle├── settings.gradle└── app       └── build.gradle

  这三个文件都有它们各自的作用,接下来我们将一一讨论。

settings文件

  如果新项目只有一个Android应用,settings.gradle应该是这样的:

include ':app'

  settings文件在初始化阶段执行,它定义了哪些模块需要被构建。在上面的例子中,app模块就会被构建。单模块项目可以不需要settings文件,但是多模块项目必须要有,否则Gralde不知道哪些模块需要被构建。
  在后台,Gradle会为每一个settings文件创建一个Settings对象,并调用其中的一些方法。没必要去了解Settings类的细节,只需要了解即可。(对Settings类的详细说明不在本书范围内,如果你想要了解,可以去Gradle文档看看)。

根目录的build文件

  根目录下的build文件是用于设置项目中的全部模块的,默认包含两部分:

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

  buildscript就是真正用来配置构建的,这点我们在第一章中提到过。其中repositories设置了JCenter为资源库。在这个例子中,资源库就是依赖库的来源,换句话说,就是我们在项目中使用的那些可下载的库的一个列表。JCenter就是一个知名的Maven资源库。
  dependencies用于设置构建程序自己的依赖关系。这意味着在这个根目录的build文件中,你不能设置你的应用和库的依赖关系。默认定义的依赖只能是Gradle的Android插件。这对每个Android模块都是必须的,因为有这个插件才能执行Android相关的task。
  allprojects用于设置那些需要添加到所有模块的配置信息。你甚至可以进一步在这部分中创建task,这些task就可以在所有的模块中使用了。
注意:一旦你使用了allprojects,所有的模块都和项目耦合上了。这就意味在没有主项目的构建文件的情况下,你不能单独构建某一个模块。刚开始你可能觉得这不是什么问题,但如果之后你打算将项目中的某个模块分离出来时,你就不得不重构构建文件了。

模块的build文件

  模块中的build文件里包含了那些只会添加到Android应用模块的配置信息。它也可以修改根目录下的build.gradle文件所设置的配置信息。结构如下:

apply plugin: 'com.android.application'android {    compileSdkVersion 22    buildToolsVersion "22.0.1"    defaultConfig {        applicationId "com.gradleforandroid.gettingstarted"        minSdkVersion 14        targetSdkVersion 22        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:22.2.0'}

  将下来我们详细介绍其中的三个部分。

Plugin

  这第一行用于添加Android应用插件,这是在根目录下的build文件中配置的依赖关系,这点我们之前已经讨论过了。这个Android插件是由Google的Android工具团队负责编写和维护的,它提供了用于构建、测试,以及打包Android应用和库的所有task。

Android

  android是该文件中最大的部分。这部分包含了整个Android相关的配置,由我们刚刚添加的Android插件来提供。
  这里面,compileSdkVersionbuildToolsVersion是必须要有的配置:

  • compileSdkVersion定义你想要用来编译你的应用的Android API版本
  • buildToolsVersion定义构建工具和编译器的版本、

  这个构建工具中有一些命令行工具,如aapt,zipalign,dx和renderscript,这些用于编译应用时生成中间件。你可以用SDK管理工具(SDK Manager)来下载这些构建工具。
  defaultConfig设置了应用的核心属性,这些属性重写了AndroidManifest.xml清单文件的相关入口:

defaultConfig {    applicationId "com.gradleforandroid.gettingstarted"    minSdkVersion 14    targetSdkVersion 22    versionCode 1    versionName "1.0"}

  第一个属性applicationId,重写了清单文件中的包名,但是这个属性与包名还是有区别的。在Gradle做为默认Android构建系统之前,AndroidManifest.mxl中的包名有两个作用:应用的唯一标识,以及R文件的包名。而Gradle用变型构建的方式让你的应用可以创建不同的版本。比如,你可以轻易的创建一个免费版本和一个付费版本。这两个版本有各自的唯一标识,这样它们就能以不同的应用出现在Google应用商店里,也可以同时安装在同一个设备上。然而,项目的源代码和生成的R文件需要保持相同的包名,否则你构建不同的版本时都要去修改它们。这就是为什么Android工具团队把包名的这两个用法给解耦了。现在,清单文件中的包名继续在你的源码和R文件使用,而用于设备和Google商店的唯一标识符的包名则用applicationId来定义。这个applicationId属性在我们尝试变型构建时会变得十分有趣的。
  defaultConfig中接下来两个属性,minSdkVersiontargetSdkVersion,看起来就很熟悉了,因为它们通常在清单文件中用< uses-sdk >元素来定义。minSdkVersion用来定义运行应用所需的最小API版本。targetSdkVersion则用来指定测试应用时使用的Android版本,且操作系统不需要向上兼容。这个属性与我们之前看到的compileSdkVersion没有关系。
  versionCodeversionName的功能和清单文件中的一样,前者是应用的版本号,后者是给用户看的版本名称。
  build文件中的所有属性值都会重写清单文件中的,所以如果你在build.gradle文件中已经定义了这些属性,就没必要再在清单文件中定义了。清单文件中的属性值将作为备选,以防build文件中没有定义这些属性。
  buildTypes用来定义如何构建应用,即用不同的构建类型来打包你的应用。我们会在第四章详细讨论这部分。

Dependencies

  dependencies 是Gradle标准配置的一部分(这也是为什么它会被放在android 之外单独一部分),它定义了应用或库的所有依赖关系。默认情况下,一个新的Android项目对libs 目录下的所有JAR文件都有依赖。但根据你在新项目向导中的选择的不同,项目也可能对AppCompat 库有依赖。这些我们会在第三章详细讨论。

初始构建task

  想要知道项目中哪些task是可用的,可以运行gradlew tasks,它会打印出所有可用task的列表。在一个新创建的Android项目中,该列表里会有Android task、构建task、构建配置task、帮助task、安装task、验证task及其它一些task。如果你不仅想看task,还想知道它们的依赖关系,可以运行gradlew tasks - -all。还可以指定一个task进行预执行,这会打印出它被执行时的所有步骤,但不会真的执行这个task,所以你可以很安全的查看某个task执行的结果。添加参数-m- -dry -run 来启动预执行。

基础task

  Gradle的Android插件使用了Java基础插件,这里面就包括了一些基础插件,它们就添加了这些标准生命周期的task和一些通用属性。这些基础插件定了assembleclean两个task,而Java基础插件又定义了checkbuild两个task。这些task在插件里都没有被实现,也不会真的被执行,它们是用来定义插件的一些属性,这些属性可以添加那些真正会被执行的task。

  • assemble:打包项目生成的文件
  • clean:清除项目生成的文件
  • check:运行所有检查程序,通常是单元测试和设备测试
  • build:同时运行assemblecheck

  Java基础插件加入了资源集的概念。Android插件以这些基础task为基础,并在它们之上加入了很多Android相关的task。

Android tasks

  Android插件继承并实现了这些基础task,所以它们在Android环境中有不同的作用:

  • assemble:生成所有构建类型的APK
  • clean:清除构建所生成的文件,比如APK文件
  • check:执行Lint检查,如果发生问题,可以中止构建
  • build:同时运行assemblecheck

  assemble 默认依赖于assembleDebugassembleRelease,以及你添加的其他构建类型的task。这就意味着,执行assemble,会构建出你添加的所有构建类型。
  除了实现这些task,Android插件还添加了一些新的task,其中重要的有以下几个:

  • connectedCheck:在已连接的设备或模拟器上运行测试程序
  • deviceCheck:其它插件的占位task,用于在远程设备运行测试程序
  • installDebuginstallRelease:在已连接的设备或模拟器上安装指定的版本
  • installuninstall:安装和卸载
      build 这个task是依赖于check 的,但并不依赖于connectedCheckdeviceCheck。这是为了使常规的检查不需要连接设备或模拟器。运行check 会生成一个Lint报告,里面有全部的警告和错误,还带有其详细说明和相关文档的链接。这个报告就在app/build/outputs 目录下的lint-reports.html 文件中,大概长这样:
    Lint Report
      在打包的时候,Lint会检查是否有可能导致应用崩溃的致命错误,如果有,它会中止构建,并在命令行界面打印出该错误。Lint还会在app/build/outputs/ 目录下生成一个名为lint-results-release-fatal.html 的报告。如果错误太多,在这个HTML报告中查看会比在命令行中方便多了。而它提供的链接也非常有用,那会让你直接链接到错误的详细说明去。

Android Studio内部

  其实你不必非得在命令行中运行Gradle的task。Android Studio中就有一个列有全部可用task的窗口,这个工具窗口名为Gradle
Gralde interface
  在这个工具窗口中双击这些task就可以运行了,还可以在Gradle Console窗口中跟进task的运行过程。如果找不到这些窗口,可以在View菜单下的Tool Window中打开它们。下图即为一个Gradle Console窗口:
Gradle Console
  你也可以在Android Studio的命令行窗口中运行task,如果你喜欢,你可以在IDE中做所有应用相关的操作。要运行命令行,你需要打开Terminal工具窗口。这是个全功能的终端,所以它可以执行任何的命令。但你可能需要先进入到项目的根目录,这样才能使用Gradle Wrapper。
提示:如果你想用其它的shell,可以修改Android Studio中的Terminal的配置。例如,微软Windows的默认终端是命令提示符,但如果你喜欢Git Bash(或者其它的shell),可以打开Android Studio的设置菜单(File –>Settings),找到Terminal,修改shell地址。比如要修改为Windows的Git Bash,就将其修改为:C:\Program Files (x86)\Git\bin\sh.exe –login -i。

自定义构建

  自定义构建程序有很多方法,而且你在Android Studio中编辑构建文件时,建议经常同步(sync)项目的Gradle文件,不管你是在自定义什么。在添加依赖或是BuildConfig域的时候,这一点尤其重要,我们很快会讨论这个问题。
  一旦你编辑了settings.gradlebuild.gradle 文件,Android Studio就会在编辑器中显示一条建议同步的信息。你也可以随时点击Tools –>Android –>Sync Project with Gradle Files,或工具栏上相应的按钮来启动同步。
SYNC
  在后台,Android Studio的同步通常会运行generateDebugSources这个task来生成所有必需的类。

操作清单文件入口

  我们已经知道,applicationIdminSdkVersiontargetSdkVersionversionCodeversionName这些属性的修改,不需要在清单文件中,而直接在构建文件中就可以进行。这里还有几个你可以操作的属性:

  • testApplicationId:APK用于设备测试的应用ID
  • testInstrumentationRunner:JUnit测试的名称,用于启动测试(见第六章)
  • signingConfig(见第四章)
  • proguardFileproguardFiles(见第九章)

Android Studio内进行操作

  除了手动的修改构建文件,你还可以在Android Studio的Project Structure中修改基本的设置。在File菜单中打开这个对话框,你可以修改项目的设置和每个模块的设置。对于每一个Android模块,你可以修改其标准Android插件的属性,或是清单的属性。在下图中,你就可以在Project Structure对话框中看到应用模块的发行版本的属性了:
Project Structure
  注意,如果你在Project Structure中做了修改,Android Studio会把这些修改写到Gradle构建文件中去。

BuildConfig和资源文件

  自从SDK工具的17版本之后,构建工具都会生成一个叫BuildConfig 的类,其中的常量DEBUG 是用来设置构建类型的。如果你想要有些代码只在debug阶段运行,比如logging,这些常就很有用。这些常量可以用来开关功能或设置服务器URL,如:

android {    buildTypes {        debug {            buildConfigField "String", "API_URL","\"http://test.example.com/api\"" buildConfigField "boolean", "LOG_HTTP_CALLS", "true"        }        release {            buildConfigField "String", "API_URL","\"http://example.com/api\"" buildConfigField "boolean", "LOG_HTTP_CALLS", "false"        }    }}

  这里的字符必须用上双引号,因为它们会生成真正的字符串。添加了buildConfigField 这行之后,你就可以在Java代码中使用BuildConfig.API_URLBuildConfig.LOG_HTTP 这两个常量了。
  最近,Android工具小组又增加了类似的方法来设置资源文件:

android {    buildTypes {        debug {            resValue "string", "app_name", "Example DEBUG"        }        release {            resValue "string", "app_name", "Example"        }    }}

  在这里的字符串就可以不加双引号了,因为在资源文件中的值都是默认加上了value=”“ 的。

项目设置

  如果你的项目中有多个Android模块,那你其实可以一次性对它们的构建文件全部进行设置,而不必一个个的设置。我们已经介绍过如何使用在根目录的构建文件中的allprojects 属性,你也可以使用同样的方法来添加Android相关的属性:

allprojects {    apply plugin: 'com.android.application'    android {        compileSdkVersion 22        buildToolsVersion "22.0.1"    }}

  只有在你的模块都是Android应用的情况下,以上设置才有用,因为你需要添加Android插件才能进行这些Android相关的设置。更好的办法是在根目录的构建文件中定义这些值,然后再把它们添加到模块中。而在Gradle中,可以在Project 对象中加入额外的属性。这就意味着,在任何的build.gradle 文件中都可以添加ext 域,以加入额外属性。
  在根目录的构建文件中的ext 域中加入定制属性:

ext {    compileSdkVersion = 22    buildToolsVersion = "22.0.1"}

  这使得模块的构建文件中的属性可以使用rootProject

android {    compileSdkVersion rootProject.ext.compileSdkVersion    buildToolsVersion rootProject.ext.buildToolsVersion}

项目属性

  使用上面的例子中的ext 域是定义额外属性的一种方法。这些属性都可以在构建的时候使用,我们在第七章讲到自定义task的时候就会用到它们。其实定义属性有很多种方法,这里我们只介绍三种常用的:

  • ext
  • gradle.properties 文件
  • -P 命令行参数

  下面这个build.gradle 文件的例子,就包含了添加额外属性的这三种方法:

ext {    local = 'Hello from build.gradle'}task printProperties << {    println local // Local extra property    println propertiesFile // Property from file    if (project.hasProperty('cmd')) {        println cmd // Command line property    }}

  而下面是相对应的gradle.properties 文件(在同一个文件夹下):

propertiesFile = Hello from gradle.properties

提示:在上面的例子中,我们创建了一个task,而task的语法我们会在第七章中介绍。
  如果你用命令来运行printProperties 这个task,会有以下输出:

$ gradlew printProperties -Pcmd=’Hello from the command line’
:printProperties
Hello from build.gradle
Hello from gradle.properties
Hello from the command line

  多亏有这些定制属性,才让构建的设置修改变得像单个属性的修改一样空易,甚至就像添加一个命令行的参数一样简单。
注意:在根目录的构建文件和模块中的构建文件中都可以定义属性,如果它们定义了同一个属性,后者会覆盖前者。

默认task

  如果你不指定task,而直接运行Gradle,它会运行help,并打印出Gradle的一些相关信息。这是因为它被设置为了Gradle的默认task。不过你也可以把默认task修改别的通用task,甚至可以是多个。
  在根目录的build.gradle 文件中添加下面一行,即可修改默认task:

defaultTasks 'clean', 'assembleDebug'

  现在,只要你不带参数运行Gradle Wrapper,就会运行cleanassembleDebug。而运行tasks 这个task就可以看到哪些task被设置为了默认task:

$ gradlew tasks | grep “Default tasks”
Default tasks: clean, assembleDebug

总结

  本章,我们详细介绍了Android Studio自动生成的那些Gradle文件。现在,你可以自己创建构建文件了,也可以自己添加所需的字段,并设置一些关键的属性。
  我们从基本构建task开始,学习了基于基本插件的Android插件,以及如何实现Android相关的task。也介绍了如何用命令行和Android Studio两种方式来运行构建task。
  在过去几年里,Android开发者的生态环境有了巨大的成长,有很多有趣的开源库可以让大家使用。在下一章,我们将学习在项目中添加依赖库的几种方法,以便我们利用那些丰富的资源。

0 0
原创粉丝点击