Android.Gradle Build Process

https://www.jianshu.com/p/7c288a17cda8

总的来说,Android的系统体系结构分为四层,自顶向下分别是:

  • 应用程序(Applications)
  • 应用程序框架(Application Frameworks)
  • 系统运行库与Android运行环境(Libraris & Android Runtime)
  • Linux内核(Linux Kernel)

安卓系统结构示意图

image.png

下面对每层进行详细说明

1. 应用程序(Applications)

Android会同一系列核心应用程序包一起发布,该应用程序包包括email客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等。所有的应用程序都是使用JAVA语言编写的。通常开发人员就处在这一层。

2. 应用程序框架(Application Frameworks)

提供应用程序开发的各种API进行快速开发,也即隐藏在每个应用后面的是一系列的服务和系统,大部分使用Java编写,所谓官方源码很多也就是看这里,其中包括:

  • 丰富而又可扩展的视图(Views),可以用来构建应用程序, 它包括列表(lists),网格(grids),文本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器。
  • 内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据
  • 资源管理器(Resource Manager)提供 非代码资源的访问,如本地字符串,图形,和布局文件( layout files )。
  • 通知管理器 (Notification Manager) 使得应用程序可以在状态栏中显示自定义的提示信息。
  • 活动管理器( Activity Manager) 用来管理应用程序生命周期并提供常用的导航回退功能。

3. 系统运行库与Android运行环境(Libraris & Android Runtime)

1) 系统运行库

Android 包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过 Android 应用程序框架为开发者提供服务。以下是一些核心库:

  • Bionic系统 C 库 - 一个从 BSD 继承来的标准 C 系统函数库( libc ), 它是专门为基于 embedded linux 的设备定制的。
  • 媒体库 - 基于 PacketVideo OpenCORE;该库支持多种常用的音频、视频格式回放和录制,同时支持静态图像文件。编码格式包括MPEG4, H.264, MP3, AAC, AMR, JPG, PNG 。
  • Surface Manager - 对显示子系统的管理,并且为多个应用程序提 供了2D和3D图层的无缝融合。这部分代码
  • Webkit,LibWebCore - 一个最新的web浏览器引擎用,支持Android浏览器和一个可嵌入的web视图。鼎鼎大名的 Apple Safari背后的引擎就是Webkit
  • SGL - 底层的2D图形引擎
  • 3D libraries - 基于OpenGL ES 1.0 APIs实现;该库可以使用硬件 3D加速(如果可用)或者使用高度优化的3D软加速。
  • FreeType -位图(bitmap)和矢量(vector)字体显示。
  • SQLite - 一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。
  • 还有部分上面没有显示出来的就是硬件抽象层。其实Android并非讲所有的设备驱动都放在linux内核里面,而是实现在userspace空间,这么做的主要原因是GPL协议,Linux是遵循该 协议来发布的,也就意味着对 linux内核的任何修改,都必须发布其源代码。而现在这么做就可以避开而无需发布其源代码,毕竟它是用来赚钱的。 而 在linux内核中为这些userspace驱动代码开一个后门,就可以让本来userspace驱动不可以直接控制的硬件可以被访问。而只需要公布这个 后门代码即可。一般情况下如果要将Android移植到其他硬件去运行,只需要实现这部分代码即可。包括:显示器驱动,声音,相机,GPS,GSM等等

2) Android运行环境

该核心库提供了JAVA编程语言核心库的大多数功能。
每一个Android应用程序都在它自己的进程中运 行,都拥有一个独立的Dalvik虚拟 机实例。Dalvik被设计成一个设备可以同时高效地运行多个虚拟系统。 Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文件针对小内存使用做了 优化。同时虚拟机是基于寄存器的,所有的类都经由JAVA编译器编译,然后通过SDK中 的 "dx" 工具转化成.dex格式由虚拟机执行。

4. Linux内核(Linux Kernel)

Android的核心系统服务依赖于Linux 2.6 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模型。 Linux 内核也同时作为硬件和软件栈之间的抽象层。其外还对其做了部分修改,主要涉及两部分修改:

  1. Binder (IPC):提供有效的进程间通信,虽然linux内核本身已经提供了这些功能,但Android系统很多服务都需要用到该功能,为了某种原因其实现了自己的一套。
  2. 电源管理:主要是为了省电,毕竟是手持设备嘛,低耗电才是我们的追求。

注:最后附上原博连接懒虫一个V:android系统体系结构,关于谷歌Android源码的目录结构并未一并贴出可在原博查阅

1.概况

  • Android APK是如何来的呢?
    怀着这个问题去查资料,发现了下边这张图。
android-build9a.png
  • 由android的项目经过编译和打包,形成了:

    1. .dex 文件
    2. resources.arsc
    3. uncompiled resources
    4. AndroidManifest.xml

    解压了一个普通的apk文件,解压出来的文件如下:

    [图片上传失败...(image-3f4e80-1520594989213)]

    classes.dex 是.dex文件。
    resources.arsc是resources resources文件。
    AndroidManifest.xml是AndroidManifest.xml文件。
    res是uncompiled resources。
    META-INF是签名文件夹。

  • META-INF其中有三个文件:

    [图片上传失败...(image-471345-1520594989213)]

    MANIFEST.MF文件
    版本号以及每一个文件的哈希值(BASE64)。包括资源文件。这个是对每个文件的整体进行SHA1(hash)。

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 2.2.0
Name: res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png
SHA1-Digest: I9s6aQ5VyOLrNo4odqSij549Oyo=
Name: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
SHA1-Digest: D6dilO+UMcglambujyMOhNbLZuY=
……

CERT.SF
这个是对每个文件的头3行进行SHA1 hash。

Signature-Version: 1.0
X-Android-APK-Signed: 2
SHA1-Digest-Manifest: QxOfCCAuQtZnHh0YRNnoxmiHT80=
Created-By: 1.0 (Android)
Name: res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png
SHA1-Digest: I9s6aQ5VyOLrNo4odqSij549Oyo=
Name: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
SHA1-Digest: D6dilO+UMcglambujyMOhNbLZuY=
……

CERT.RSA
这个文件保存了签名和公钥证书。

2. 具体打包过程

[图片上传失败...(image-cbaba7-1520594989214)]

2.1 aapt阶段

  • 使用aapt来打包res资源文件,生成R.java、resources.arsc和res文件(二进制 & 非二进制如res/raw和pic保持原样)

  • res目录有9种目录
    --animator。这类资源以XML文件保存在res/animator目录下,用来描述属性动画。
    --anim。这类资源以XML文件保存在res/anim目录下,用来描述补间动画。
    --color。这类资源以XML文件保存在res/color目录下,用描述对象颜色状态选择子。
    --drawable。这类资源以XML或者Bitmap文件保存在res/drawable目录下,用来描述可绘制对象。例如,我们可以在里面放置一些图片(.png, .9.png, .jpg, .gif),来作为程序界面视图的背景图。注意,保存在这个目录中的Bitmap文件在打包的过程中,可能会被优化的。例如,一个不需要多于256色的真彩色PNG文件可能会被转换成一个只有8位调色板的PNG面板,这样就可以无损地压缩图片,以减少图片所占用的内存资源。
    --layout。这类资源以XML文件保存在res/layout目录下,用来描述应用程序界面布局。
    --menu。这类资源以XML文件保存在res/menu目录下,用来描述应用程序菜单。
    --raw。这类资源以任意格式的文件保存在res/raw目录下,它们和assets类资源一样,都是原装不动地打包在apk文件中的,不过它们会被赋予资源ID,这样我们就可以在程序中通过ID来访问它们。例如,假设在res/raw目录下有一个名称为filename的文件,并且它在编译的过程,被赋予的资源ID为R.raw.filename,那么就可以使用以下代码来访问它:

Resources res = getResources();  
InputStream is = res .openRawResource(R.raw.filename);  

--values。这类资源以XML文件保存在res/values目录下,用来描述一些简单值,例如,数组、颜色、尺寸、字符串和样式值等,一般来说,这六种不同的值分别保存在名称为arrays.xml、colors.xml、dimens.xml、strings.xml和styles.xml文件中。
--xml。这类资源以XML文件保存在res/xml目录下,一般就是用来描述应用程序的配置信息。

  • R.java文件

    [图片上传失败...(image-3c05df-1520594989213)]

    这就是R.java的源代码,里面拥有很多个静态内部类,比如layout,string等。
    每当有这种资源添加时,就在R.java文件中添加一条静态内部类里的静态常量类成员,且所有成员都是int类型。

    [图片上传失败...(image-b7f577-1520594989213)]

    里面的资源可以有两种方法引用:
    1.在java程序中引用资源按照java的语法来引用即:R.resource_type.resource_

    name注意:resource_name不需要文件的后缀名
    2.在XML文件中引用资源格式:@[package:]type/name

  • resources.arsc文件
    resources.arsc这个文件记录了所有的应用程序资源目录的信息,包括每一个资源名称、类型、值、ID以及所配置的维度信息。我们可以将这个resources.arsc文件想象成是一个资源索引表,这个资源索引表在给定资源ID和设备配置信息的情况下,能够在应用程序的资源目录中快速地找到最匹配的资源。

2.2 aidl阶段

  • AIDL (Android Interface Definition Language), Android接口定义语言,Android提供的IPC (Inter Process Communication,进程间通信)的一种独特实现。
    这个阶段处理.aidl文件,生成对应的Java接口文件。

2.3 Java Compiler阶段

  • 通过Java Compiler编译R.java、Java接口文件、Java源文件,生成.class文件。

2.4 dex阶段

  • 通过dex命令,将.class文件和第三方库中的.class文件处理生成classes.dex。

2.5 apkbuilder阶段

  • 将classes.dex、resources.arsc、res文件夹(res/raw资源被原装不动地打包进APK之外,其它的资源都会被编译或者处理)、Other Resources(assets文件夹)、AndroidManifest.xml打包成apk文件。
    注意
    res/raw和assets的相同点:
    1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
    res/raw和assets的不同点:
    1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
    2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

2.6 Jarsigner阶段

  • 对apk进行签名,可以进行Debug和Release 签名。

2.7 zipalign阶段

  • release mode 下使用 aipalign进行align,即对签名后的apk进行对齐处理。
    Zipalign是一个android平台上整理APK文件的工具,它对apk中未压缩的数据进行4字节对齐,对齐后就可以使用mmap函数读取文件,可以像读取内存一样对普通文件进行操作。如果没有4字节对齐,就必须显式的读取,这样比较缓慢并且会耗费额外的内存。
    在 Android SDK 中包含一个名为 “zipalign” 的工具,它能够对打包后的 app 进行优化。 其位于 SDK 的 build-tools 目录下, 例如: D:\Develop\Android\sdk\build-tools\23.0.2\zipalign.exe

是什么?

在语法上是基于Groovy语言的(Groovy 是一种基于JVM的敏捷开发语言,可以简单的理解为强类型语言java的弱类型版本),在项目管理上是基于Ant和Maven概念的项目自动化建构工具。

基础知识准备

Java基础,命令行使用基础
官方文档https://docs.gradle.org/current/dsl/
** Gradle使用指南:** https://gradle.org/docs/current/userguide/userguide
Android插件文档https://github.com/google/android-gradle...
AndroidGradle使用文档http://tools.android.com/tech-docs/new-build-system/user-guide
Groovy基础: http://attis-wong-163-com.iteye.com/blog/1239819
Groovy闭包的Delegate机制http://www.cnblogs.com/davenkin/p/gradle-learning-3.html

搭建Gradle运行环境

  1. Gradle 运行依赖JVM,也就是java运行的环境。所以要安装jdk和jre,好像目前的Gradle的运行环境要求jdk的版本在1.6以上,应该的,现在jdk都到1.8了。
  2. 然后到Gradle官网现在Gradle的压缩包。地址,这个页面里面又两种方式,一种手动安装,一种通过脚本安装。我一般喜欢自己动手,这样将来清理起来比较方便。
  3. 下载压缩包后,解压,然后配置环境变量,手动安装过jdk的人应该都配置环境变量很熟了吧。每个平台下配置环境变量的方式不一样

MacOS 下配置。在~/.bash_profile中添加如下代码

#gradle  注意gradle-2.14.1是自己解压的路径
export GRADLE_HOME=${HOME}/gradle-2.14.1
PATH=${PATH}:${GRADLE_HOME}/bin
export PATH

保存后在终端输入source ~/.bash_profile回车执行让刚刚的配置生效。然后命令行输入gradle -v查看是否安装成功。

$ gradle -v

------------------------------------------------------------
Gradle 2.14.1
------------------------------------------------------------

Build time:   2016-07-18 06:38:37 UTC
Revision:     d9e2113d9fb05a5caabba61798bdb8dfdca83719

Groovy:       2.4.4
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_111 (Oracle Corporation 25.111-b14)
OS:           Mac OS X 10.12.2 x86_64

弄一个HelloWorld看看

创建一个test_gralde文件夹。然后在文件夹里面创建一个build.gradle文件。注意文件名不要乱起。在build.gradle中添加如下代码:

task helloworld{
    doLast{
        println'Hello World!'
    }
}
#后者等同于下面的代码,
task helloworld2 <<{
    println "Hello World!"
}

然后来运行一下:

liuqiangs-MacBook-Pro:test_gralde liuqiang$ gradle helloworld
:helloworld
Hello World!

BUILD SUCCESSFUL

Total time: 1.52 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html

我们分析一下执行步骤。build.gradle是Gradle默认的构建脚本文件,执行Gradle命令的时候,会默认加载当前目录下的build.gradle脚本文件,当然你也可以通过 -b 参数指定想要加载执行的文件。这只是个最简单的task例子,后面详细介绍task的常见定义。

这个构建脚本定义一个任务(Task),任务名字叫helloworld,并且给任务helloworld添加了一个动作,官方名字是Action,阅读Gradle源代码你会到处见到它,其实他就是一段Groovy语言实现的闭包,doLast就意味着在Task执行完毕之后要回调doLast的这部分闭包的代码实现。第二个方法中的“<<”表示向helloworld中加入执行代码。至于语法部分,基本是Groovy语法(包括一些语法糖,也就是写简写方式,如果写个JavaScript或者Python会好理解一些,但是还是建议去读一下groovy的基本语法),加上一些DSL(domain specific language)的约定。

执行流程和基本术语

和Maven一样,Gradle只是提供了构建项目的一个框架,真正起作用的是Plugin。Gradle在默认情况下为我们提供了许多常用的Plugin,其中包括有构建Java项目的Plugin,还有Android等。与Maven不同的是,Gradle不提供内建的项目生命周期管理,只是java Plugin向Project中添加了许多Task,这些Task依次执行,为我们营造了一种如同Maven般项目构建周期。

Gradle是一种声明式的构建工具。在执行时,Gradle并不会一开始便顺序执行build.gradle文件中的内容,而是分为两个阶段,第一个阶段是配置阶段,然后才是实际的执行阶段。
配置阶段,Gradle将读取所有build.gradle文件的所有内容来配置Project和Task等,比如设置Project和Task的Property,处理Task之间的依赖关系等。

看一个基本结构的Android多Moudule(也就是gradle中的多Project Multi-Projects Build)的基本项目结构。

├── app #Android App目录
│   ├── app.iml
│   ├── build #构建输出目录
│   ├── build.gradle #构建脚本
│   ├── libs #so相关库
│   ├── proguard-rules.pro #proguard混淆配置
│   └── src #源代码,资源等
├── module #Android 另外一个module目录
│   ├── module.iml
│   ├── build #构建输出目录
│   ├── build.gradle #构建脚本
│   ├── libs #so相关库
│   ├── proguard-rules.pro #proguard混淆配置
│   └── src #源代码,资源等
├── build
│   └── intermediates
├── build.gradle #工程构建文件
├── gradle
│   └── wrapper
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell脚本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置

上面的是完整的AndroidStudio中的项目结构,我们抽象成Gradle多个Project的样子

├── app 
│   ├── build.gradle #构建脚本
├── module 
│   ├── build.gradle #构建脚本
├── build.gradle #工程构建文件
├── gradle
│   └── wrapper    #先不去管它
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell脚本
├── gradlew.bat
└── settings.gradle #工程配置

  • Gradle为每个build.gradle都会创建一个相应的Project领域对象,在编写Gradle脚本时,我们实际上是在操作诸如Project这样的Gradle领域对象。在多Project的项目中,我们会操作多个Project领域对象。Gradle提供了强大的多Project构建支持。
  • 要创建多Project的Gradle项目,我们首先需要在根(Root)Project中加入名为settings.gradle的配置文件,该文件应该包含各个子Project的名称。Gradle中的Project可以简单的映射为AndroidStudio中的Module。
  • 在最外层的build.gradle。一般干得活是:配置其他子Project的。比如为子Project添加一些属性。
  • 在项目根目录下有个一个名为settings.gradle。这个文件很重要,名字必须是settings.gradle。它里边用来告诉Gradle,这个multiprojects包含多少个子Project(可以理解为AndroidStudio中Module)。

读懂Gradle配置语法

Gradle向我们提供了一整套DSL,所以在很多时候我们写的代码似乎已经脱离了groovy,但是在底层依然是执行的groovy所以很多语法还是Groovy的语法规则。
看一个AndroidStudio中app下的build.gradle的配置

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "me.febsky.demo"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    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:25.1.0'
}

分析第一行apply plugin: 'com.android.application'

这句其实是Groovy语法糖,像Ruby和Js都有这种语法糖,apply实际上是个方法,补上括号后的脚本:apply (plugin: 'com.android.application'),看起来还是有点别扭是不?还有个语法糖,如果方法参数是个map类型,那么方括号可以省略,进一步还原apply([ plugin: 'com.android.application']),不理解的可以去看下Groovy的map的写法,和js一样。所以这行的意思是:apply其实是个方法,接收一个Map类型的参数。

总结两点:1. 方法调用,圆括号可以省略 2. 如果方法参数是个Map,方括号可以省略。

Groovy语言的闭包语法

看上面的dependencies 这其实是个方法调用。调用了Project的dependencies方法。只不过参数是个闭包,闭包的用法在文章开始给出了链接。我们对其进行还原一下:

#方法调用省略了()我们加上
dependencies ({
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.1.0'
})

提示一点:如果闭包是方法的最后一个参数,那么闭包可以放在圆括号外面

#所以代码还能写成这样
dependencies (){
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.1.0'
}

Getter和Setter

Groovy语言中的两个概念,一个是Groovy中的Bean概念,一个是Groovy闭包的Delegate机制。
Java程序员对JavaBeans和Getter/Setter方法肯定不陌生,被设计用来获取/设置类的属性。但在Groovy中就不用那些没用的方法了。即Groovy动态的为每一个字段都会自动生成getter和setter,并且我们可以通过像访问字段本身一样调用getter和setter。比如Gradle的Project对象有个version属性(Property)下面这两行代码执行结果是一样的:

println project.version // Groovy  
println(project.getVersion()) // Java  

Project,Task ,Action

Gradle的Project之间的依赖关系是基于Task的,而不是整个Project的。

Project:是Gradle最重要的一个领域对象,我们写的build.gradle脚本的全部作用,其实就是配置一个Project实例。在build.gradle脚本里,我们可以隐式的操纵Project实例,比如,apply插件、声明依赖、定义Task等,如上面build.gradle所示。apply、dependencies、task等实际上是Project的方法,参数是一个代码块。如果需要,也可以显示的操纵Project实例,比如:project.ext.myProp = 'myValue'

Task:被组织成了一个有向无环图(DAG)。Gradle中的Task要么是由不同的Plugin引入的,要么是我们自己在build.gradle文件中直接创建的。Gradle保证Task按照依赖顺序执行,并且每个Task最多只被执行一次。

Gradle在默认情况下为我们提供了几个常用的Task,比如查看Project的Properties、显示当前Project中定义的所有Task等。可以通过一下命令行查看Project中所有的Task:$ gradle tasks (具体log不再贴出来)。可以看到,Gradle默认为我们提供了dependencies、projects和properties等Task。dependencies用于显示Project的依赖信息,projects用于显示所有Project,包括根Project和子Project,而properties则用于显示一个Project所包含的所有Property。

**Tips: **查看Project中所有的Task:$ gradle tasks
查看Project中所有的properties:$ gradle properties

在上面的build.gradle中加入如下代码:

task myTask {  
    doFirst {  
        println 'hello'  
    }  
    doLast {  
        println 'world'  
    }  
}  

这段代码的含义:给Project添加一个名为“myTask”的任务
用一个闭包来配置这个任务,Task提供了doFirst和doLast方法来给自己添加Action。

其实build.gradle脚本的真正作用,就是配置一个Project实例。在执行build脚本之前,Gradle会为我们准备好一个Project实例,执行完脚本之后,Gradle会按照DAG依次执行任务。

自定义Task的写法

看下面代码文件路径~/Test/build.gradle

#1
task helloWorld << {
    println "Hello World"
}
#2 Test文件夹下建一个src目录,建一个dst目录,src目录下建立一个文件,命名为test.txt
task copyFile(type: Copy){
    from "src"
    into "dst"
}

第一个这里的helloWorld是一个DefaultTask类型的对象,这也是定义一个Task时的默认类型,当然我们也可以显式地声明Task的类型,甚至可以自定义一个Task类型。
第二个代码中(type:Copy)就是“显式地声明Task的类型”,执行gradle copyFile test.txt也跑到dst中去了。

如果task声明在根Project的build.gradle中的allprojects()方法中,那么这个Task会应用于所有的Project。

task的依赖关系

Gradle不提供内建的项目生命周期管理,只是java Plugin向Project中添加了许多Task,这些Task依次执行,为我们营造了一种如同Maven般项目构建周期。那么这些task是如何依次执行的这就用到声明的依赖关系taskA.dependsOn taskB看下面代码:

task taskA << {
   println 'this is taskA from project 1'
}

task taskB << {
   println 'this is taskB from project 1'
}

taskA.dependsOn taskB

然后我们在命令行运行:
$ gradle taskA
运行结果会先执行taskB的打印,然后执行taskA的打印

如果是Muliti-Project的模式,依赖关系要带着所属的Project,如taskA.dependsOn ':other-project:taskC' 其中taskC位于和taskA不同的Project中,相对于AndroidStudio来说,就是位于不同的Module下的build.gradle中,而other-project为Module名字。

Task 的type可以自定义(没有深入研究)

自定义Plugin的写法

没有深入研究,给出一个网上的例子:

apply plugin: DateAndTimePlugin

dateAndTime {
    timeFormat = 'HH:mm:ss.SSS'
    dateFormat = 'MM/dd/yyyy'
}

class DateAndTimePlugin implements Plugin<Project> {
    //该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
    //比如向其中加入Task,定义额外的Property等。
    void apply(Project project) {
        project.extensions.create("dateAndTime", DateAndTimePluginExtension)

        project.task('showTime') << {
            println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
        }

        project.tasks.create('showDate') << {
            println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
        }
    }
}
//每个Gradle的Project都维护了一个ExtenionContainer,
//我们可以通过project.extentions进行访问
//比如读取额外的Property和定义额外的Property等。
//向Project中定义了一个名为dateAndTime的extension
//并向其中加入了2个Property,分别为timeFormat和dateFormat
class DateAndTimePluginExtension {
    String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
    String dateFormat = "yyyy-MM-dd"
}

每一个自定义的Plugin都需要实现Plugin接口,除了给Project编写Plugin之外,我们还可以为其他Gradle类编写Plugin。该接口定义了一个apply()方法,在该方法中,我们可以操作Project,比如向其中加入Task,定义额外的Property等。

原文地址

Gradle Wrapper

Wrapper,顾名思义,其实就是对Gradle的一层包装,便于在团队开发过程中统一Gradle构建的版本,然后提交到git上,然后别人可以下载下来,这样大家都可以使用统一的Gradle版本进行构建,避免因为Gradle版本不统一带来的不必要的问题。(所以要明白这个东西可以没有,有了只是为了统一管理,更加方便)

生成wrapper

gradle 内置了生成wrapper的task,我们可以命令行下执行:
$ gradle wrapper

生成后的目录结构如下(用过AndroidStudio的很熟悉了):

├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

  • gradlew和gradlew.bat分别是Linux和Window下的可执行脚本,他们的用法和gradle原生命令是一样的,gradle怎么用,他们也就可以怎么用。在MacOS下运行$ ./gradlew myTask
  • gradle-wrapper.jar是具体业务逻辑实现的jar包,gradlew最终还是使用java执行的这个jar包来执行相关gradle操作。
  • gradle-wrapper.properties是配置文件,用于配置使用哪个版本的gradle等

详细的看下gradle-wrapper.properties内容

#Sat Jan 21 14:02:40 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip

从上面内容和文件的名称都可以看出,这就是个java的配置文件,上面看到的是自动生成的,我们也可以手动修改。然后看下各个字段的含义:

  • distributionBase 下载的gradle压缩包解压后存储的主目录
  • distributionPath 相对于distributionBase的解压后的gradle压缩包的路径
  • zipStoreBase 同distributionBase,只不过是存放zip压缩包的
  • zipStorePath 同distributionPath,只不过是存放zip压缩包的
  • distributionUrl gradle发行版压缩包的下载地址,也就是你现在这个项目将要依赖的gradle的版本。

生成wrapper可以指定参数

  • 生成wrapper可以通过指定参数的方式来指定gradle-wrapper.properties内容。
  • 使用方法如gradle wrapper –gradle-version 2.14这样,这样就意味着我们配置wrapper使用2.14版本的gradle,它会影响gradle-wrapper.properties中的distributionUrl的值,该值的规则是http://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip
  • 如果我们在调用gradle wrapper的时候不添加任何参数呢,那么就会使用你当前Gradle的版本作为生成的wrapper的gradle version。例如你当前安装的gradle是2.10版本的,那么生成的wrapper也是2.10版本的。注:当前版本指的是环境变量中配置的那个版本。

【参考文章】
http://www.infoq.com/cn/articles/android-in-depth-gradle/
http://blog.csdn.net/innost/article/details/48228651

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,822评论 25 707
  • 说明 本文主要介绍和Gradle关系密切、相对不容易理解的配置,偏重概念介绍。部分内容是Android特有的(例如...
    jzj1993阅读 15,604评论 1 62
  • 前言 为什么需要学Gradle? Gradle 是 Android 现在主流的编译工具,虽然在Gradle 出现之...
    真笨笨鱼阅读 1,489评论 0 0
  • 你说你洗了一桶衣服,我开玩笑的说你洗的时候怎么没和我说下我也有一桶,你开始发脾气说为什么你做什么事要想着我?凭什么...
    谢笑阅读 280评论 0 1
  • 当用户与程序的界面进行交互时,或者通过代码控制一些东西时,UIKit中会发生一系列复杂的事件来处理这张交互。在这一...
    解放者莫雷尔阅读 192评论 0 0