Android之Gradle的理解

一.前言

Gradle作为Android官方指定的插件脚本,有必要去了解了解。这里说说作者在学习过程中遇到难以理解的知识点。

这里强烈推荐以下三部曲

1.groovy学习

2.gradle学习

3.来龙去脉

一定要先去认真拜读这三篇,结合android项目去学习,相信掌握的也差不多了,存在的疑惑点兴许从我这篇文章能找到你想要的答案。

二.自定义任务

Gradle中的Task要么是由不同的Plugin引入的,要么是我们自己在build.gradle文件中直接创建的

在当前工程中定义Task类型,只能应用在当前module中,没什么卵用。

下面是全局可用:

在单独的项目中定义Task类型项目中存在大量的自定义Task类型时,在另外的一个gradle文件中定义这些Task,然后再apply到build.gradle文件中。

all.gradle:

task helloWorld << {

   println description
}

可以参考印象笔记的demo https://github.com/evernote/evernote-sdk-android

一个Task除了执行操作之外,还可以包含多个Property,其中有Gradle为每个Task默认定义了一些Property,比如description,logger等。另外,每一个特定的Task类型还可以含有特定的Property,比如Copy的from和to等。当然,我们还可以动态地向Task中加入额外的Property。在执行一个Task之前,我们通常都需要先设定Property的值,如下:初始化task的helloworld的默认属性description

helloWorld.configure {
   description = "this is helloWorld" 
}

三.自定义属性property

Gradle还为我们提供了多种方法来自定义Project的Property。
在build.gradle文件中定义Property
添加一个名为property1的Property:
ext.property1 = "this is property1"
或者采用闭包的形式
ext {
   property2 = "this is property2"
}

使用的时候并可以不用加ext直接使用属性

    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"

    }

注意:rootProject.ext.android表示是在rootProject下的xxxx.gradle中定义了属性为android 如项目下增加config.gradle

config.gradle:

ext {
   //这是一个map属性
    android = [compileSdkVersion: 21,
               buildToolsVersion: "25.0.2",
               applicationId    : "com.wangzhi.MaMaHelp",
               minSdkVersion    : 14,
               targetSdkVersion : 21,
               versionCode      : 264,//221
               versionName      : "7.5.53",
               environment      : false //打包时,正式环境为false ,打alpha、beta、正式三个包时为true
    ]

}

在项目下的build.gradle中添加如下就可以加载了:

apply from: "config.gradle"//使用config.gradle文件(就是一个groovy类)调用apply()

四.生命周期hook

有两种方式可以编写回调(hook)生命周期事件:
  1.将代码写在闭包中
  2.实现Gradle API所提供的监听器接口
具体Gradle提供了哪些钩子可以通过官方文档API查看,下面使用与task执行图有关的钩子来学习生命周期钩子的使用。

Task执行图project.gradle.taskGraph
在配置阶段,gradle决定了在执行阶段要运行的task顺序。这个顺序被建模为一个表示依赖关系的有向无环图(DAG)。图中的每个Task被称为一个节点,且该图没有闭环,也就是说先前执行的task不会再次执行。
Gradle API对task执行图有对应的领域模型对象,可以通过project.gradle.taskGraph 来访问task执行图对象。

方法一:通过action hook

WhenReady:hook点
调用taskGraph的whenReady(Closure)方法,可以在taskGraph生成完成(也就是有向无环图构建完成)之后执行该方法传入的闭包。
gradle.taskGraph.whenReady{ TaskExecutionGraph taskGraph -> //当任务准备好之后会执行这个action
if(taskGraph.hasTask(release)) { //查看taskGraph中是否有发布任务
    if(!version.release){
        version.release = true
        ant.propertyfile(file: versionFile) {
            entry(key: 'release',type:'string' , operation: '=' , value:'true')
        }
    }
}
}

方法二:通过实现TaskExecutionGraphListener监听起监听生命周期

实现taskGraph监听器来挂接
通过监听器挂接到生命周期需要两步:
编写一个类来实现特定的监听器接口
用于监听taskGraph事件的接口是由TaskExecutionGraphListener接口提供的,其他的事件监听接口不列举,用到的时候查阅API。
需要实现TaskExecutionGraphListener接口的graphPopulated(TaskExecutionGraph)方法,具体用法如下:

class ReleaseVersionListener implements TaskExecutionGraphListener{
final static String releaseTaskPath = ':release'
@Override
void graphPopulated(TaskExecutionGraph taskGraph){
    if(taskGraph.hasTask(releaseTaskPath)){ //确定release task是否包含在taskGraph中
        List<Task> allTasks = taskGraph.allTasks
        Task releaseTask = allTask.find{it.path == releaseTaskPath}
        Project project = releaseTask.project //通过task来访问project
        if(!project.version.release){
            project.version.release = true
            project.ant.propertyfile(file: project.versionFile) {
                entry(key: 'release',type:'string' , operation: '=' , value:'true')
            }
        }
    }
}
}
在监听器中不能直接访问Project实例,但是可以通过GradleAPI,通过task来访问该task所属的project。注册监听器实现使用project.gradle.addListener(listener: Object)方法或者

project.gradle.taskGraph.addTaskExecutionGraphListener(listener: TaskExecutionGraphListener) 来注册监听器,

代码如下:.gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener())

五.gradle执行流程

gradle就是脚本文件,需要触发命令(如 gradle taskName等)会从上到下执行,只是执行之前gradle要先构建自身(得到gradle实例)--包装命令中的参数---初始化settings.gradle---配置(各个项目中的build.gradle)----执行执行流程settings.gradle --->项目的build.gradle--->app.gradle(会先执行app的所有依赖(包括自定义的plugins)再执行app.build的配置)settings.gradle:用来指定哪些 module 参与构建
第一:gradle构建自身:gradle实例化
    
public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
    ......
    private DefaultGradleLauncher doNewInstance(StartParameter startParameter, GradleLauncher parent,
                                                BuildCancellationToken cancellationToken, BuildRequestMetaData requestMetaData, BuildEventConsumer buildEventConsumer, final BuildSessionScopeServices sessionScopeServices, List<?> servicesToStop) {
        BuildScopeServices serviceRegistry = BuildScopeServices.forSession(sessionScopeServices);
        ......
        //Gradle框架自身初始化OK以后第一次调用时parent为null。
        GradleInternal parentBuild = parent == null ? null : parent.getGradle();
        //创建一个DefaultGradle对象,也就是Gradle的实现,其中最重要的参数就是startParameter,包含了我们执行gradle命令时携带的参数。
        GradleInternal gradle = serviceRegistry.get(Instantiator.class).newInstance(DefaultGradle.class, parentBuild, startParameter, serviceRegistry.get(ServiceRegistryFactory.class));
        //把实例化的Gradle对象传入DefaultGradleLauncher实例。
        DefaultGradleLauncher gradleLauncher = new DefaultGradleLauncher(
            gradle,
            ......
        );
        nestedBuildFactory.setParent(gradleLauncher);
        return gradleLauncher;
    }
    ......
}

第二:构建Settings对象和Project对象
 NotifyingSettingsLoader.java
 
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
        //Settings对象创建
        SettingsInternal settings = settingsLoader.findAndLoadSettings(gradle);
        ......
		//projects对象创建是通过Settings完成的:settings.getDefaultProject()
        buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
        gradle.getBuildListenerBroadcaster().projectsLoaded(gradle);
        return settings;
    }
第三:根据Stags.xxxx枚举实例,进行init、configure、build

//此时upTo参数值为枚举的Stage.Build
private void doBuildStages(Stage upTo) {
    if (stage == Stage.Build) {
        //状态控制,避免多次状态build,第一次执行该方法时stage为null。
        throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
    }
    //第一次实例化调用该方法时stage=null,所以构建生命周期进入Load状态(初始化阶段)。
    if (stage == null) {
        // Evaluate init scripts
        initScriptHandler.executeScripts(gradle);


        // Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
        settings = settingsLoader.findAndLoadSettings(gradle);
        //标记当前状态为Load状态(初始化阶段)。
        stage = Stage.Load;
    }


    if (upTo == Stage.Load) {
        return;
    }


    if (stage == Stage.Load) {
        //如果当前stage是Load状态(初始化阶段),则接着构建生命周期进入Configure状态(配置阶段)。
        // Configure build
        buildOperationExecutor.run("Configure build", new ConfigureBuildAction());
        //标记当前状态为Configure状态(配置阶段)。
        stage = Stage.Configure;
    }


    if (upTo == Stage.Configure) {
        return;
    }
    //Load状态(初始化阶段)和Configure状态(配置阶段)完后进入Build状态(执行阶段)。
    // After this point, the GradleLauncher cannot be reused
    stage = Stage.Build;


    // Populate task graph
    buildOperationExecutor.run("Calculate task graph", new CalculateTaskGraphAction());


    // Execute build
    buildOperationExecutor.run("Run tasks", new RunTasksAction());
}
围绕整个 Gradle 构建过程的核心是几个委托实例对象:
      构建类型             生成的委托实例对象
Build script          Project
Init script              Gradle                一个项目只有一个
Settings script         Settings              一个项目只有一个 
编写 Gradle 脚本时获取 Gradle 实例后进行的设置其实都是在对该对象进行设置的
编写 Gradle 脚本时获取 Settings 实例后进行的设置其实都是在对该对象进行设置
编写 Gradle 脚本时获取对应不同的 Project 实例后进行的设置其实都是在对该对象进行设置

六.自定义插件

自定义插件:https://juejin.im/entry/577bc26e165abd005530ead8

在 Android Studio 中 Maven 仓库相关的概念和应用。
  Maven 包
  Maven 仓库
  发布包到本地仓库
  发布包到 Bintray Jcenter 远程仓库
  发布包到 Sonatype MavenCentral 远程仓库
  搭建私有 Sonatype 仓库
  搭建私有 Artifacotory 仓库
对于 Android 开发者而言,只需要知道 Maven 是一种构建工具,Maven 包是由所谓 POM(Project Object Model)所定义的文件包格式即可。
Gradle 可以使用 Maven 包,而且大部分的 Android 能够使用的远程依赖包都是 Maven 包。
对于一个合符规范的 Maven Package,pom 文件、aar(或者 jar) 文件是必须的。
maven包:可以代表一个jar,此生成这个jar的lib可能会依赖很多jar包,这些依赖都是由POM其实是个xml来定义依赖和格式的。比如规定groupId/articleId/versiong以及托管在maven的是aar还是jar包形式
Maven 仓库:Maven 包集中存放的地方
这些仓库,可以是放在本地,也可以放在某个远程服务器上。 可以是私有仓库,也可以是公开的。下面是笔者日常开发用的库列表:
mavenCentral();//mavenCentral 是最早的 maven 中央仓库
jcenter()//jcenter 是 Android Studio 0.8 版本起的默认 maven 中央仓库
maven {//是笔者的本机的仓库
     url 'file:///Users/my-user-name/Documents/Android/repo/'
}
maven {//笔者部署在内网服务器的私有仓库
     url 'http://192.168.99.100:8081/content/repositories/releases/'
}
maven { //是可以把 github 项目发不成 maven 库(jitpack 本身是一个很酷的 idea)
    url "https://jitpack.io" 
}
maven { url : xxx},这种格式可以配置任何一个存在的仓库;只要将包制作成符合maven格式的包,就可以将包放在任何地方,只需要使用maven { url : xxx}格式就可以使用包

一个符合规范的 maven 包至少包含 pom 文件和主文件包。难道这些都要手动编写和创建么?

除了官方的插件之外,我们可以自定义自己的插件,实现简单逻辑:

1.打包插件

  插件是一个类,继承自 org.gradle.api.Plugin 接口,重载 void apply(Project project) 方法,这个方法将会传入使用这个插件的 project 的实例,这是一个重要的 context。
  (1) 首先,你得新建一个Android Project
  (2) 然后再新建一个Module,这个Module用于开发Gradle插件,同样,Module里面没有gradle plugin给你选,但是我们只是需要一个“容器”来容纳我们写的插件,因此,你可以随便选择一个Module类型(如Phone&Tablet Module或Android Librarty),因为接下来一步我们是将里面的大部分内容删除,所以选择哪个类型的Module不重要。
  (3) 将Module里面的内容删除,只保留build.gradle文件和src/main目录。
      由于gradle是基于groovy,因此,我们开发的gradle插件相当于一个groovy项目。所以需要在main目录下新建groovy目录
  (4) groovy又是基于Java,因此,接下来创建groovy的过程跟创建java很类似。在groovy新建包名,如:com.hc.plugin,然         后在该包下新建groovy文件,通过new->file->MyPlugin.groovy来新建名为MyPlugin的groovy文件。
  (5) 为了让我们的groovy类申明为gradle的插件,新建的groovy需要实现org.gradle.api.Plugin接口。
  (6) 现在,我们已经定义好了自己的gradle插件类,接下来就是告诉gradle,哪一个是我们自定义的插件类,因此,需要在               main目录下新建resources目录,然后在resources目录里面再新建META-INF目录,再在META-INF里面新建gradle-                 plugins目录。最后在gradle-plugins目录里面新建properties文件,注意这个文件的命名,你可以随意取名,但是后面使          用这个插件的时候,会用到这个名字。比如,你取名为com.hc.gradle.properties,而在其他build.gradle文件中使用自定          义的插件时候则需写成:apply plugin: 'com.hc.gradle'
      使用插件方式二:import 插件所在类,然后apply plugin 插件名
  (7) 因为我们要用到groovy以及后面打包要用到maven,所以在我们自定义的Module下的build.gradle需要添加如下代码:
      apply plugin: 'groovy'
      apply plugin: 'maven'

      dependencies {
        compile gradleApi()//gradle sdk
    compile localGroovy()//groovy SDK
}
repositories {
       mavenCentral()

     }

2.当前项目的插件

前面我们讲了如何自定义gradle插件并且打包出去,可能步骤比较多。有时候,你可能并不需要打包出去,只是在这一个项目中使用而已,那么你无需打包这个过程。
只是针对当前项目开发的Gradle插件相对较简单。步骤之前所提到的很类似,只是有几点需要注意:
1.新建的Module名称必须为BuildSrc 注意setting.build中不需要include这个Module

2.无需resources目录,直接使用的module中apply plugin "包名.自定义的插件"

   如:apply plugin:com.hc.second.SecondPlugin

当前build.gradle中定义使用:

class ApkDistExtension {
    Closure nameMap = null;
    String destDir = null;
}


class ApkDistPlugin implements Plugin<Project> {


    @Override
    void apply(Project project) {


        project.extensions.create('apkdistconf', ApkDistExtension);//创建一个ApkDistExtension实例为apkdistconf,这个实例必须和声明的一致


        project.task(name:'apkdist' , type: project.tasks.HelloWorld) << {//如果不依赖,则需要单独指定运行这个apkdist任务 :./gradle -p /app apkdist,否则这个task不会被执行,所以自定义的任务要继承check、clean、build等任务才会被执行
            println 'hello, world!'


            def closure = project['apkdistconf'].nameMap;
            closure('wow!');


            println project['apkdistconf'].destDir
        }
    }
}


apply plugin: ApkDistPlugin


apkdistconf {//创建是会执行这里初始化操作
    println 'init,hello' 
    nameMap { name ->
        println 'hello, ' + name
        return name
    }
    destDir 'your-distribution-directory'
}

七.跳跃式笔记

Gradle是一种声明式的构建工具。Gradle的DSL只是Groovy语言的内部DSL,也必须遵循Groovy的语法规则。

Gradle本身的领域对象主要有Project和Task。
Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。
一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。
另外,一个Task可以读取和设置Project的Property以完成特定的操作。是不是很屌的样子。

task关键字其实是一个groovy中方法的调用,该方法属于Project,而大括号之间的内容则表示传递给task()方法的一个闭包。

无论在哪个 Gradle 工程模块的脚本中打印 gradle 或者 getGradle() 对象的 hashCode 都是同一个对象,而一个 settings.gradle 一 一对应一个 Settings 实例对象,一个 project module 的 build.gradle 一 一对应一个 Project 对象。
所以我们在编写 Gradle 脚本时要时刻铭记这个分析结论,因为记住这个结论对于我们 Gradle 脚本的编写和理解非常重要,很多人写不明白 Gradle 脚本的实质其实就是分不清哪个对象有啥 API,哪个 API 属于哪个层次的对象。

gradle中定义的buildScript、dependencies、apply、sourceSets等等关键字都是方法的调用
如:学习总结,环境搭建
在使用Gradle时,我们并没有像上面的parent.configChild()一样指明方法调用的对象,而是在build.gradle文件中直接调用task(),apply()和configuration()等方法。这是因为在没有说明调用对象的情况下,Gradle会自动将调用对象设置成当前Project。
比如调用apply()方法和调用project.apply()方法的效果是一样的。查查Gradle的Project文档,你会发现这些方法都是Project类的方法。
对于configurations()方法,该方法实际上会将所跟闭包的delegate设置成ConfigurationContainer,然后在该ConfigurationContainer上执行闭包中的代码。再比如,dependencies()方法,该方法会将所跟闭包的delegate设置成DependencyHandler。

Gradle最常用的Plugin便是java Plugin了。和其他Plugin一样,java Plugin并没有什么特别的地方,只是向Project中引入了多个Task和Property。当然,
java Plugin也有比较与众不同的地方,其中之一便是它在项目中引入了构建生命周期的概念,就像Maven一样。但是,和Maven不同的是,Gradle的项目构建生命周期并不是Gradle的内建机制,而是由Plugin自己引入的。
一个项目总会依赖于第三方,要么是一个第三方类库,要么是自己开发的另一个module
配置Gradle的Repository,就是告诉Gradle在什么地方去获取这些依赖

Gradle对依赖进行分组,允许编译时使用一组依赖,运行时使用另一组依赖。每一组依赖称为一个Configuration,在声明依赖时,我们实际上是在设置不同的Configuration。
要定义一个Configuration,我们可以通过以下方式完成:studio一般不需要设置,应该是有默认的,即为classpath
configurations {
   myDependency
}
通过dependencies()方法向myDependency中加入实际的依赖项:
dependencies {
//下面的myDependency是关键
   myDependency 'org.apache.commons:commons-lang3:3.0'
}
//类似studio中的classpath
dependencies {
   classpath 'com.android.tools.build:gradle:1.3.0'
}
//还有 这里的compile,testCompile
dependencies {
    compile project(':library')
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
    compile 'com.evernote:android-intent:1.0.1'
    testCompile 'junit:junit:4.8.2' 
}
myDependency,classpath,compile,testCompile都是Configuration(一组依赖)。
除了myDependency都不使我们定义的,为啥呢,android Plugin会自动定义compile和testCompile分别用于编译Java源文件和编译Java测试源文件。classpath应该是用于所有,我类推的。
Gradle还允许我们声明对其他Project或者文件系统的依赖。
dependencies {
//library是另一个module的名字
   compile project(':library')
}
allprojects {
    repositories {
        jcenter()
    }
    //通常studio项目没有,咱自己加的
   apply plugin: 'idea'
   task allTask << {
      println project.name
   }
}
allprojects()方法将repositories配置一次性地应用于所有的module(子Project)和root-project本身,当然也包括定义的Task,这个task配置到所有module里面了和root-project。

subprojects()方法用于配置所有的子Project(不包含根Project)
Gradle本身只是一个架子,真正起作用的是Task和Plugin。

Groovy语言中的两个概念,一个是Groovy中的Bean概念,一个是Groovy闭包的delegate机制。也就是说,在配置阶段,将.gradle中的内容转化生成groovy,并配置依赖关系。

在执行时,Gradle并不会一开始便顺序执行build.gradle文件中的内容,而是分为两个阶段,第一个阶段是配置阶段,然后才是实际的执行阶段。

配置阶段:Gradle将读取所有build.gradle文件的所有内容来配置Project和Task等,比如设置Project和Task的Property,处理Task之间的依赖关系等。

执行阶段:真正执行的是groovy编译后的字节码

Action就是一个闭包,但闭包不特指action 
   NamedDomainObjectContainer<T>:注意这个类:这个玩意可以创建一个T的新实例,一般作为参数的T传递比如Action<? super NamedDomainObjectContainer<SourceSets>>>()
   凡是有这种方法的:xxx(Action<? super NamedDomainObjectContainer<BuildType>> action){}
   gradle代码中就可以这么使用就有很多类似如下的:
   signingConfigs {
        Mall {
            keyAlias 'keystore'
            keyPassword '2012wangzhi'
            storeFile file('../app/keystore.keystore')
            storePassword '2012wangzhi'
        }
APP{
}
    }
    buildTypes {//调用buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action)
        debug {//生成buildTypes实例为debug,添加到容器中,并配置闭包
            minifyEnabled false
            shrinkResources false
            manifestPlaceholders = [
                    channelValue: "DEBUG"
            ]
            signingConfig signingConfigs.Mall


            buildConfigField("String", "NetAddr", "\"alpha.\"")
            buildConfigField("boolean", "Base_Debug", "true")
            buildConfigField("String", "PUBLIC_FROM", "\"public\"")
            buildConfigField("String", "HOST_URL", "\"http://open.alpha.lmbang.com\"")
        }
        release {//生成buildTypes实例为release,添加到容器中,并配置闭包
            minifyEnabled false
            shrinkResources false
            signingConfig signingConfigs.Mall


            if (!rootProject.ext.android.environment) {
                buildConfigField("String", "NetAddr", "\"\"")
                buildConfigField("boolean", "Base_Debug", "false")
                buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.lmbang.com\"")
                buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
        }
    }
    productFlavors {//调用buildTypes(Action<? super NamedDomainObjectContainer<productFlavors>> action)
        
            Alpha {//生成productFlavors实例为Alpha,添加到容器中,并配置闭包
                buildConfigField("String", "NetAddr", "\"alpha.\"")
                buildConfigField("boolean", "Base_Debug", "true")
                buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.alpha.lmbang.com\"")
                buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
          Beta {//生成productFlavors实例为Beta,添加到容器中,并配置闭包
                buildConfigField("String", "NetAddr", "\"beta.\"")
                buildConfigField("boolean", "Base_Debug", "true")
              buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.beta.lmbang.com\"")
              buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
            Online {//生成productFlavors实例为Online,添加到容器中,并配置闭包
                buildConfigField("String", "NetAddr", "\"\"")
                buildConfigField("boolean", "Base_Debug", "false")
                buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.lmbang.com\"")
                buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
        }
   
   
   当我们使用 task myTask{ xxx}的时候。花括号是一个 closure。这会导致gradle 在创建这个 Task 之后,返回给用户之前,会先执行 closure 的内容。
   当我们使用 task myTask << {xxx}的时候,我们创建了一个 Task 对象,同时把 closure 做为一个 action 加到这个 Task 的 action 队列中,并且告诉它“最后才执行这个 closure”(注意,<<符号是 doLast 的代表)。
   
   //dependsOn是一个函数,下面这句话的意思是 clean任务依赖cposCleanTask任务。所以  
   //当你gradle clean以执行clean Task的时候,cposCleanTask也会执行  
   clean.dependsOn 'cposCleanTask'  clean是插件的的任务,cposCleanTask是自定义的任务,进行依赖关系,当执行Clean任务时会先执行依赖cposCleanTask
   每一个build.gradle文件都会转换成一个Project对象。在Gradle术语中,Project对象对应的是Build Script。
   /* 
     最关键的内容来了: buildTypesScriptBlock. 
     buildTypes和上面的signingConfigs,当我们在build.gradle中通过{}配置它的时候, 
     其背后的所代表的对象是NamedDomainObjectContainer<BuildType> 和 
     NamedDomainObjectContainer<SigningConfig> 
     注意,NamedDomainObjectContainer<BuildType/或者SigningConfig>是一种容器, 
     容器的元素是BuildType或者SigningConfig。我们在debug{}要填充BuildType或者 
    SigningConfig所包的元素,比如storePassword就是SigningConfig类的成员。而proguardFile等 
    是BuildType的成员。 
    那么,为什么要使用NamedDomainObjectContainer这种数据结构呢?因为往这种容器里 
    添加元素可以采用这样的方法: 比如signingConfig为例 
    signingConfig{//这是一个NamedDomainObjectContainer<SigningConfig> 
       test1{//新建一个名为test1的SigningConfig元素,然后添加到容器里 
         //在这个花括号中设置SigningConfig的成员变量的值 
       } 
      test2{//新建一个名为test2的SigningConfig元素,然后添加到容器里 
         //在这个花括号中设置SigningConfig的成员变量的值 
      } 
    } 
    在buildTypes中,Android默认为这几个NamedDomainObjectContainer添加了 
    debug和release对应的对象。如果我们再添加别的名字的东西,那么gradleassemble的时候 
    也会编译这个名字的apk出来。比如,我添加一个名为test的buildTypes,那么gradle assemble 
    就会编译一个xxx-test-yy.apk。在此,test就好像debug、release一样。 
   */ 

   使用gradle命令:
   
   1.gradle tasks 查看当前项目或module已有的所有任务
   2.gradle properties 查看当前项目或module已有的可用属性
   3.gradle -h         查看帮助文档
   4.gradle projects 查看工程信息 会根据include的数量+1 项目本身也是一个project 一个build.gradle对应一个project


 Gradle工作包含三个阶段:

首先是初始化阶段。对我们前面的multi-project build而言,就是执行settings.gradle
Initiliazation phase的下一个阶段是Configration阶段。
Configration阶段的目标是解析每个project中的build.gradle。比如multi-project build例子中,解析每个子目录中的build.gradle。在这两个阶段之间,我们可以加一些定制化的Hook。这当然是通过API来添加的。
Configuration阶段完了后,整个build的project以及内部的Task关系就确定了。恩?前面说过,一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。所以,我们可以添加一个HOOK,即当Task关系图建立好后,执行一些操作。
最后一个阶段就是执行任务了。当然,任务执行完后,我们还可以加Hook。

Gradle提供了一种名为extra property的方法。extra property是额外属性的意思,在第一次定义该属性的时候需要通过ext前缀来标示它是一个额外的属性。
定义好之后,后面的存取就不需要ext前缀了。ext属性支持Project和Gradle对象。即Project和Gradle对象都可以设置ext属性
如:
ext {
    android = [compileSdkVersion: 21,
               buildToolsVersion: "25.0.2",
               applicationId    : "com.wangzhi.MaMaHelp",
               minSdkVersion    : 14,
               targetSdkVersion : 21,
               versionCode      : 264,//221
               versionName      : "7.5.53",
               environment      : false //打包时,正式环境为false ,打alpha、beta、正式三个包时为true
    ]
}
  当一个Project apply一个gradle文件的时候,这个gradle文件会转换成一个Script对象。这个,相信大家都已经知道了。
Script中有一个delegate对象,这个delegate默认是加载(即调用apply)它的Project对象。但是,在apply函数中,有一个from参数,还有一个to参数(参考图31)。通过to参数,你可以把delegate对象指定为别的东西。
delegate对象是什么意思?当你在Script中操作一些不是Script自己定义的变量,或者函数时候,gradle会到Script的delegate对象去找,看看有没有定义这些变量或函数。
gradle文件中包含一些所谓的Script Block(姑且这么称它)。Script Block作用是让我们来配置相关的信息。不同的SB有不同的需要配置的东西。这也是我最早说的行话。比如,源码对应的SB,就需要我们配置源码在哪个文件夹里


插件的源码可以使用 Groovy、Scala、Java 三种语言,笔者不会 Scala,所以平时只是使用 Groovy 和 Java。
前者用于实现与 Gradle 构建生命周期(如 task 的依赖)有关的逻辑,后者用于核心逻辑,表现为 Groovy 调用 Java 的代码。

Gradle 采用了 Groovy 语言作为主要的脚本语言。一个 build.gradle 文件,其实是一个 Groovy 类
  如settings.gradle 和 build.gradle 在 Gradle 平台中,其实都是一个 Groovy 对象。
    Gradle 通过插件(plugin)的方式来支持构建。插件是很多任务(task)的集合,task 中又包含了许多 action。
Groovy 是一个基于 JVM 的语言,代码最终编译成字节码(bytecode)在 JVM 上运行
├── app                     //app module
│   ├── build.gradle        //app module 的 build.gradle
├── build.gradle            //项目 build.gradle,通常配置项目全局配置,如 repositories 和 dependencies
├── gradle.properties       //项目属性文件,通常可以放置一些常量
├── lib                     //lib module
│   ├── build.gradle        //lib module 的 build.gradle
└── settings.gradle         //项目总体设置,通常是配置项目中所有的 module




Gradle 的核心代码,只提供了一个框架,具体的功能(如构建 Android 工程)是通过插件机制来实现的。
Gradle 提供了大量官方的插件,如 Maven、Groovy、Java、Publishing、Signing等,也有大量第三方的插件(Android),甚至每个人都可以自己实现一个插件(如 笔者开发的 Bugtags 插件,这个将在最后一篇讲述)。
这些 plugin 定义了一系列的 task、DSL 和约定,在build.gradle 文件使用这些 plugin:
apply plugin: java 或者apply plugin: 'groovy' 引用java或者groovy插件 因为想要使用java或groovy插件中固有的DSL(特定领域语言,顾名思义只能应用指定的地方)比如特有的属性、方法等等使用jar包中的方法就要载入jar包
当你写了一个独立的 file_uri.gradle 文件,你可以通过:
apply from: 'file_uri.gradle'

Gradle最常用的Plugin便是java Plugin了。和其他Plugin一样,java Plugin并没有什么特别的地方,只是向Project中引入了多个Task和Property。当然,
java Plugin也有比较与众不同的地方,其中之一便是它在项目中引入了构建生命周期的概念,就像Maven一样。但是,和Maven不同的是,Gradle的项目构建生命周期并不是Gradle的内建机制,而是由Plugin自己引入的。
一个项目总会依赖于第三方,要么是一个第三方类库,要么是自己开发的另一个module
配置Gradle的Repository,就是告诉Gradle在什么地方去获取这些依赖


Gradle对依赖进行分组,允许编译时使用一组依赖,运行时使用另一组依赖。每一组依赖称为一个Configuration,在声明依赖时,我们实际上是在设置不同的Configuration。
要定义一个Configuration,我们可以通过以下方式完成:studio一般不需要设置,应该是有默认的,即为classpath
configurations {
   myDependency
}
通过dependencies()方法向myDependency中加入实际的依赖项:
dependencies {
//下面的myDependency是关键
   myDependency 'org.apache.commons:commons-lang3:3.0'
}
//类似studio中的classpath
dependencies {
   classpath 'com.android.tools.build:gradle:1.3.0'
}
//还有 这里的compile,testCompile
dependencies {
    compile project(':library')
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
    compile 'com.evernote:android-intent:1.0.1'
    testCompile 'junit:junit:4.8.2' 
}
myDependency,classpath,compile,testCompile都是Configuration(一组依赖)。
除了myDependency都不使我们定义的,为啥呢,android Plugin会自动定义compile和testCompile分别用于编译Java源文件和编译Java测试源文件。classpath应该是用于所有,我类推的。
Gradle还允许我们声明对其他Project或者文件系统的依赖。
dependencies {
//library是另一个module的名字
   compile project(':library')
}
allprojects {
    repositories {
        jcenter()
    }
    //通常studio项目没有,咱自己加的
   apply plugin: 'idea'
   task allTask << {
      println project.name
   }
}
allprojects()方法将repositories配置一次性地应用于所有的module(子Project)和root-project本身,当然也包括定义的Task,这个task配置到所有module里面了和root-project。


subprojects()方法用于配置所有的子Project(不包含根Project)
Gradle本身只是一个架子,真正起作用的是Task和Plugin。

学习api文档:

android 插件api文档

gradle的api文档

gradle DSL for Android

学习博客

https://juejin.im/entry/577bc26e165abd005530ead8

http://kvh.io/cn/tags/EmbraceAndroidStudio/

https://www.aliyun.com/jiaocheng/1117895.html

https://blog.csdn.net/blueangle17/article/details/60872224

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值