Android Gradle(三)- gradle

上一篇对groovy语法与核心api做了简单总结,为gradle具体业务功能实现做了语言铺垫,那么接下来进入到gradle api的学习。

一、gradle生命周期

gradle生命周期分为三个阶段:
Initialization初始化阶段: 各工程初始化,解析settings.gradle,构建所有project对应的project对象。
Configuration配置阶段: 执行所有工程中build.gradle的配置代码,解析所有project对象中的task,构建task的拓扑图。Task的执行顺序在配置阶段就确定好了。
Execution执行阶段: 执行task及其依赖task。

gradle部分生命周期监听回调

//配置阶段开始前的监听回调
this.beforeEvaluate {
}

//配置阶段完成以后的回调
this.afterEvaluate {
}

//当前project build完成
this.gradle.buildFinished {
}

//project构建之前
this.gradle.beforeProject {
}

//project构建之后
this.gradle.afterProject {
}

//其他监听
this.gradle.addBuildListener(new BuildListener() {
    @Override
   void buildStarted(Gradle gradle) {
    }

    @Override
   void settingsEvaluated(Settings settings) {
    }

    @Override
   void projectsLoaded(Gradle gradle) {
    }

    @Override
   void projectsEvaluated(Gradle gradle) {
    }

    @Override
   void buildFinished(BuildResult result) {
    }
})
二、 project

gradle-5.4.1/src/core-api/org/gradle/api/Project.java
gradle中根工程和各module对应一个project,它是脚本入口。

project相关api
this.getProject() or this.project//获取project
this.getRootProject() or this.rootProject//获取根project
this.getSubprojects() or this.subprojects//获取所有子project 根project获取的子project即各个module
this.getParent() or this.parent//获取父project

在build.gradle中方法的定义及调用
this.helloGradle()

def helloGradle() {
    def allprojects = this.getAllprojects()
    allprojects.each { project ->
        println 'project name :' + project.name
   }
}

//对app project 进行配置
project('app') { Project project ->
   apply plugin: ‘...'
   group ‘...'
   version ‘...'
   dependencies {...}
   android {…}
   ...
}
属性相关api

Project自带属性

public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
    /**
    * The default project build file name.
    */
   String DEFAULT_BUILD_FILE = "build.gradle";
  
 /**
    * The hierarchy separator for project and task path names.
    */
   String PATH_SEPARATOR = ":";

   /**
    * The default build directory name.
    */
   String DEFAULT_BUILD_DIR_NAME = "build";
   String GRADLE_PROPERTIES = "gradle.properties";
   String SYSTEM_PROP_PREFIX = "systemProp";
   String DEFAULT_VERSION = "unspecified";
   String DEFAULT_STATUS = "release";
}

//使用ext闭包拓展属性
ext {
    compileSdkVersion = 29
}
//这里修改compileSdkVersion为例
Android{
    compileSdkVersion [this.project.compileSdkVersion](http://this.project.compilesdkversion/)
}

//在gradle中引入文件
apply from :this.file('common.gradle’)

//在gradle中引入插件
apply plugin:'gradle-plugin’

//property基本操作
getProperty(‘')
setProperty('')
hasProperty(‘')

另外还可以在gradle.properties中配置 key - value形式的全局属性
#key-value
isLoadTest=true
# 这里可以以key-value的形式定义全局的属性,使用的时候需要用toBoolean() toInteger()等转换
# 另外属性命名不能与已有方法重名,这样容易找不到属性

//三方库引入方式对比
2.X
compile: 编译运行期都参与。
provided: 编译期参与,运行时不参与
3.0之后:
compile 延伸出api 和 implementation。
api : 与compile效果一样。
implementation : implementation依赖的库只能自己库本身访问,例如:A依赖B,B依赖C,如果B依赖C是使用的implementation依赖,那么在A中是访问不到C 中的方法的,如果需要访问,请使用api依赖。其他与compile一致。
compileOnly: 与provided效果一样。
其他:
runtimeOnly: 和apk效果一样,打包时有效,不参与编译。
testImplementation:和testCompile效果一样,在单元测试和打包测试apk的时候有效。
debugImplementation:和debugCompile效果相同, 在debug模式下有效。
releaseImplementation:和releaseCompile效果相同,只在release模式和打包release包情况下有效。
文件相关api

路径:

//工程根目录
getRootDir().absolutePath //or this.rootDir

//工程目录/build
getBuildDir().absolutePath //or this.buildDir

//module目录
getProjectDir().absolutePath //or this.projectDir

//获取文件
getFile('build.gradle')
def getFile(String path) {
    try {
        //new file要传入绝对路径,file()传入相对路径,相对于当前project开始查找
       def file = file(path)//project的file方法获取文件,files方法定位多个符合条件文件
       return file.text
   } catch (GradleException e) {
        println 'file not found'
   }
    return null
}

//文件拷贝 from into
copy {
    from file('build/outputs/apk/')
    into getRootProject().getBuildDir().path + '/apk/'
   //附加功能
   exclude {} //包含
   rename {} //重命名
}

//对文件树进行遍历
fileTree('build/outputs/apk/') { FileTree filetree ->
    filetree.visit { FileTreeElement element ->
        println 'the file name is:' + element.file.name
       copy {
            from element.file
           into getRootProject().getBuildDir().path + 'test'
       }
    }
}
其他
buildscript { ScriptHandler scriptHandler ->
    //gradle本身配置工程的仓库地址
   scriptHandler.repositories {}
    //gradle本身配置工程的三方库依赖地址
   scriptHandler.dependencies {}
    //对应project中的dependencies,是配置应用程序三方库的依赖
}
三、task

gradle-5.4.1/src/core-api/org/gradle/api/Task.java
一个Task表示构建的单个原子动作。构建工作就是由各种Task构成的。

新建task两种写法:
//用task函数创建
task myTask {
    println 'hello task'
}

//使用taskContainer容器创建
this.tasks.create(name: 'myTask') {
    println 'hello task'
}

task所有可配置信息
public interface Task extends Comparable<Task>, ExtensionAware {
   String TASK_NAME = "name";
   String TASK_DESCRIPTION = "description";
   String TASK_GROUP = "group";
   String TASK_TYPE = "type";
   String TASK_DEPENDS_ON = "dependsOn";
   String TASK_OVERWRITE = "overwrite";
   String TASK_ACTION = "action";
...
}

task在执行阶段的两个回调:
doFirst{}//在已有的task之前去添加逻辑
doLast{}//在已有的task之后去添加逻辑

调用方式:
task myTask {
    doLast{ //内部调用
    }
}
//or
myTask.doLast{ //外部调用
}

小实例:计算prebuild到build的总时长
def startTime, endTime
//配置阶段执行完以后
this.afterEvaluate { Project project ->
    //保证要找的task配置完成
   def preBuildTask = project.tasks.getByName('preBuild')
    preBuildTask.doFirst {
        startTime = System.currentTimeMillis()
    }

    def buildTask = project.tasks.getByName('build')
    buildTask.doLast {
        endTime = System.currentTimeMillis()
        println "the build time is  ${endTime - startTime}"
   }
}

gradle在配置阶段会构建task的拓扑图,这个过程组织好task的执行顺序
task依赖

dependsOn配置依赖

task taskX {
   doLast {
       println 'taskX'
   }
}

静态配置
//taskY在taskX之后执行
task taskY(dependsOn:taskX) {
   doLast {
       println 'taskY'
   }
}

//or
task taskY {
    dependsOn taskX
    doLast {
        println 'taskY'
   }
}

//or
taskY.dependsOn taskX

指定依赖之后,执行taskY,会先执行它依赖的taskX ,然后执行它自己。

动态配置
task taskZ() {
    dependsOn this.tasks.findAll { task ->
        return task.name.startsWith('lib')//动态依赖lib开头的task
   }
    doLast {
        println 'taskZ'
   }
}
task执行顺序

mustRunAfter()、shouldRunAfter()
只指定顺序执行,没有依赖关系。

task taskZ {
   mustRunAfter taskX //强制task在指定的task之后执行
   shouldRunAfter taskX //不强制性的
   doLast {
        println 'taskZ'
   }
}

如果单独执行taskZ,那么就只会执行taskZ,跟taskX和taskY无关,只有taskX、taskY、taskZ都执行的时候,这个条件能约束执行顺序。

finalizedBy

taskX.finalizedBy taskY //执行完taskX之后会拉起执行taskY

总结:dependsOn指定依赖关系,先执行依赖的task再执行自己;finalizedBy是当前task执行完成拉起让自己结束的task;而mustRunAfter是多个执行的task之前约束一个执行顺序,默认情况下无依赖关系的task执行顺序是随机的。

task输入输出

TaskInputs、TaskOutputs

输入输出在Gradle中用于增量构建,执行一个任务,如果在编译时,gradle判断在从上一次编译中,该task的输入输出没有任何改变,那么gradle就会跳过该task,前提是这个task至少有一个输入或输出。

支持的输入输出类型参考TaskInputs接口
inputs.file/files/property
outputs.file/files

四、Settings

在gradle中,通过执行Settings类来完成构建的初始化阶段。
最重要的是inlcude方法

include ':app', ':lib_network’ //配置工程子project
rootProject.name = ‘GradleDemo'

这里可以做条件判断是否引入子project
if (hasProperty('isLoadTest') ? isLoadTest.toBoolean() : false) {
    include ':Test'
}

对应两个监听
gradle.settingsEvaluated {
   println “Settings.gradle 初始化执行结束"
}

gradle.projectsLoaded {Gradle gradle ->
   println “Settings.gradle所有在 settings 中 include 的 Project 对象都创建完成了"
}
五、SourceSet
//5.4.1版本配置方法
android {
    sourceSets {
        // sourceSets的main对象,该对象属于默认对象。
        main {
            resources {
                srcDirs 'src/main/res', 'src/main/res-ad', 'src/main/res-player'
           }
        }
    }
}

//3.0版本配置方法
// 配置源码路径。这个sourceSets是java插件引入的
sourceSets {
   // 还可以根据productFlavors中的其他渠道配置对应的sourceSets对象。编译时,会首先加载对应productFlavors中的资源,没有则加载main中默认资源。
   main {
        manifest.srcFile 'AndroidManifest.xml'// 这是一个函数
       java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
        jniLibs.srcDirs = ['libs']
    }

groovy和gradle文章写下来,基本是代码的形式,可能阅读并不是那么直观,但是这个比较符合我的习惯。方便查询吧。

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

推荐阅读更多精彩内容