Gradle For Android(2)--基础的定制构建

理解Gradle文件

当创建一个新的Project的时候,会默认生成3个Gradle文件。在项目的根目录(在Project的Top-Level)下会生成settings.gradlebuild.gradle。而在Android app模块中会创建一个build.gradle文件。目录结构如下:

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

Settings文件

对于一个新的Project,settings.gradle文件只会有一行

include ':app'

这个setting.gradle在初始化阶段被执行,并且定义了哪些Module应该在构建中被包含。在该例中,只有:app模块被包含。只有一个模块的Project可以不需要该文件,而多个模块的Project的必须要该文件,否则Gradle不知道哪些模块需要被包含(include)。

在这种场景下,Gradle创建了为每个Settings文件都创建了一个Serttings对象,并且可以从该对象中调用所需要的Methods。我们不需要知道Settings类的细节,但是最好关注一下。

顶层的build.gradle

顶层的build.gradle文件中,我们可以配置一些options,这些options可以应用于所有在这个Project中的Module。它默认包含以下两个代码块:

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

buildscript代码块中是真正构建的配置的地方。respositories代码块配置了JCenter作为仓库。在这种情况下,仓库代表了这个Project所依赖的资源或者说我们所需要的一些可下载的Libraries都是保存在这个仓库中。JCenter是一个知名的Maven仓库。

dependencies代码块用来配置构建过程的依赖。也就是说,我们不应该在Top-Level的build.gradle中包含Application或者Libraries的依赖。它唯一的依赖关系应该为是默认定义的Android Plugin。它会去遍历每一个Android Module,因为它是会执行Android-Related Tasks的插件。

allprojects代码块用来定义需要被应用到每一个Module中的属性。我们甚至可以在这个代码块中创建Task,而这些Task可以在各个Module中被应用。

Module中的build.gradle

Module层的build.gradle文件包含了一些options,这些options只能应用在Android app module中。它也能够覆盖Project层的build.gradle文件中的属性。该模块的file如下:

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的application plugin,配置在顶层的build.gradle中。这个插件主要由Android工具团队写并且维护的,提供了所有需要构建application以及libraries的build,test,package任务
  • android:这个代码块主要包括了Android特殊的配置。只有两个属性需要配置compileSdkVersion以及buildToolsVersion。负责编译的sdk api版本以及build tools的版本。其中build tools包括了很多命令行的工具,比如说aapt,zipalign,dx,renderscript等等,使用这些工具我们可以生产出各种各样的中间件。我们可以通过SDK Manager下载build tools。

defaultConfig代码块配置了App核心的属性。在这个代码块中的属性会重写AndroidManifest.xml中相对应的属性。

applicationId属性会重写Manifest.xml中的packageName。在Gradle之前的构建系统中,PackageName有两个作用,唯一表示一个App以及用于为R.java赋予包名。而通过Gradle使用build variants使得构建不同版本的App变得更加简单了。比如,很容易构建一个付费/免费的版本。而这两个版本需要两个单独的PackageName,这样才能够被一起装到一个手机上。但是源代码以及R文件包名都还保持着相同的PackageName,以至于在构建多个版本的时候,需要把所有的源文件都进行修改。

因此,这也就是为什么Android Tool团队减弱了packageName的这两个用途。定义在Manifest中的PackageName仍然会用于SourceCode以及R文件。而Google Play则会使用application id作为唯一标识符来区分App。

包括minSdkVersion,targetSdkVersion,versionCode,versionName等等在内的所有值都会覆盖掉Manifest.xml中的值,如果在build.gradle中定义了这些值,Manifest.xml中就可以不定义了,但是以防万一最好还是在Manifest.xml中定义好,避免遗漏出错。

buildType代码块定义了构建不同类型的App的地方。后续会再详细说明。

dependencies代码块是标准Gradle配置的一部分,这也就是它为什么会在android代码块之外的原因。并且它定义了app或者library中所有的依赖关系。默认一个新的Android App会对libs目录下的所有jar包有依赖。取决于新Project的启动项配置。

了解Tasks

为了了解整个Project中哪些Task是可用的,我们可以通过gradlew tasks来列出所有可用的Tasks。如下图所示:

可用的Tasks

在一个新创建的Android Project中,它包括了:

  • Android tasks
  • build tasks
  • build setup tasks
  • help tasks
  • install Tasks
  • verification Tasks
  • ...

如果不止想看到Tasks,而是各个Task之间的依赖关系,可以使用gradlew tasks --all。当你希望打印出执行一个特殊的Task的所有步骤时,可以加上参数-m或者--dry-run

Android Tasks

Android Plugin继承自基础的Task,并且实现了自己一些功能。这些Tasks在Android中会有如下表现:

  • assemble:为每个Build Type构建APK
  • clean:移除所有Build中间件以及Apk文件等等
  • check:执行Lint的检查,并且如果Lint出现问题的时候,会打断Build过程
  • build:执行assemble以及check任务

Assemble任务默认由assembleDebug以及assembleRelease构成,如果有更多的Build Type的话,则会有更多的任务。也就是说,执行Aseemble将会为每个Build Type触发一个构建。

除了继承了这些Tasks之外,Android Plugin也添加了一些新的Task。以下为最重要的新的Tasks:

  • connectedCheck:在已经连接的设备或者模拟器上执行tests任务
  • deviceCheck:为其他插件在远程设备上调试提供的占位任务
  • installDebug/installRelease:在已经连接的设备或者模拟器上安装一个特定的版本
  • 所有的install任务都会有相对应的uninstall任务

build任务依赖于check任务,而不是connectedCheck或者deviceCheck。这保证了常规的检查不需要连接设备 。执行完check任务后,会生成一个Lint Report文件,其中保存着warnings以及errors,可以在app/build/outputs/lint-results.html中找到。

Lint Report

当Assemble一个Release版本时,Lint将检查可能会导致App Crash的问题。如果找到的话,就会中断Build,并且在Command-Line中打印出错误。并且也会在app/build/outputs中生成lint-results-release-fatal.html文件。如果有多个错误,则通过HTML的Report报告然后滑动到报错的位置就可以看到了。

在Android Studio中,右侧的Gradle窗口双击对应的Task即可开始执行。也就不用在命令行工具中输入命令了。

Gradle窗口

BuildConfig以及Resources

从SDK Tool版本17之后,Build Tool会生成一个名为BuildConfig的类,其中包含了根据build type生成的DEBUG常量。这对控制日志打印是非常有利的。而且,这也为Debug或者Release的常量区分带来了很多的方案,比如我们需要根据Build Type来开启/关闭一些Features,或者设置Server的URLs等等,例如:

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"
          } 
      }
}

通过双引号中添加的String值会被生成一个真实的String。通过添加了buildConfigField这一行,我们可以使用BuildConfig.API_URLBuildConfig.LOG_HTTP来引用不同的值。

而最近,Android Tool团队也会通过以下方式来配置资源:

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

Project级别的Settings

如果拥有多个Android Modules的话,它可以非常简便的修改每个Module中的build.gradle中的值,而不用手动的去修改了。我们已经看到了allprojects代码块在顶层的build.gradle中定义了reositories,并且你可以使用相同的方式来应用Android指定的Settings:

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

这段代码块只会应用于所有的Android App Project,因为需要应用Android Plugin去使用Android特殊的Settings。一种更好的方案是在顶层的build.gradle中定义这些值,然后在各个Module中应用。也就意味着,我们可以在build.gradle文件中绑定ext代码块,其中定义一些自定义的属性:

ext {
       compileSdkVersion = 22
       buildToolsVersion = "22.0.1"
}

通过这种方式来在Module级别的build.gradle中使用rootProject来获取使用的值。

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

Project properties

定义Project的Properties有几种方式,最常用的三种:

  • 使用ext代码块
  • 使用gradle.properties文件
  • 通过-P的命令行参数

以下为这三种方式的示例代码:

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

如果通过命令行参数执行printProperties任务的话,输出如下:

$ gradlew printProperties -P cmd='Hello from the command line'
:printProperties
Hello from build.gradle
Hello from gradle.properties
Hello from the command line

默认的任务

如果使用gradle没有指定具体的任务的话,则会执行help任务。如果需要指定默认的任务的话,则需要在顶层的build.gradle中加入默认任务:

defaultTasks 'clean', 'assembleDebug'

这样的话,执行gradlew就会默认执行这两个任务。而通过gradlew tasks | grep "Default tasks"也可以查看默认的任务。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,918评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,709评论 2 59
  • 上一章我们学习了Gralde的使用,创建和转换Android工程。本章我们将深入了解构建文件,学习一些有用的tas...
    sollian阅读 1,370评论 0 3
  • 说明 本文主要介绍和Gradle关系密切、相对不容易理解的配置,偏重概念介绍。部分内容是Android特有的(例如...
    jzj1993阅读 15,604评论 1 62
  • 设计模式整体可简单描述为:六大基本原则、三大类23种设计模式。1、六大基本原则:1)单一职责原则:大到一个模块,中...
    无边小猪阅读 1,106评论 0 1