hi各位小伙伴,前面《在飞Android Gradle实战》之生命周期1我们已经介绍了Gradle的生命周期。
只有了解了gradle的生命周期才能写出正确的脚本代码。初始化阶段gradle会完成所有工程的初始化,决定我们的项目有多少个子项目 这个阶段重点就是解析setting.gradle文件,初始化阶段完毕就是配置阶段,build.gradle中的代码大部分都是执行在配置阶段的,配置阶段执行完之后就是运行阶段,在 运行阶段段真正执行task中的逻辑,只有知道这三个阶段的作用,才能写出正确的脚本代码,否则代码很容易执行再错误的生命周期。
前言:
了解完生命周期之后,还需要了解一个核心内容就是Project,Project是脚本的入口,只有了解了它才能开始真正的脚本开发。
还是一样的风格,只讲实战中经常用到的内容,让你快速入门和使用,加上我自己的分析让你更加清楚的了解这些内容。至于基础知识请自己学习~。
本章难度:简单 普通 困难
本章重要程度:普通 重要 核心
一:什么是Project
在Android Studio的Terminal中调用./gradlew projects就可以看到你项目中有哪些project了。
上图中显示,我们的项目有3个project。
project有个特征,就是每个里面都有一个build.gradle文件。所以可以通过它来判断是否是project。
如上图,由三个project组成:Root poject:'dap-audience-network' 2个子project:'buildSrc'、'ToolboxSample'
二:Project API
1.getAllProjects() 获取所有project,包括自己。getSubproject()获取子project,不包括自己
//====获取所有project 类似./gradlew projects====
def getProjects() {
println '-----'
//eachWithIndex是遍历
this.getAllprojects().eachWithIndex { Project project, int index ->
println "project:${project.name}"//输出project的Name
}
println '-------'
//获取子project
this.getSubprojects().eachWithIndex { Project subProject, int index ->
println "project_subject:${subProject.name}"//输出subproject的Name
}
println '-------'
def rootName = this.getRootProject().name
println "root_project:${rootName}"//输出根project的名字
}
this.getProjects()
2.使用Project API管理project
//针对project'ToolboxSample'这个project操作配置些内容
project('ToolboxSample'){
apply plugin 'com.android.application' //输出格式是application
group 'com.inmoc' //指定分组
version '1.0.1' //指定版本
dependencies{ //配置依赖模块
}
android{} //配置android模块
}
=====
//针对自己及子project配置
allprojects{}
//只包括子工程,不包含自己
subprojects{Project project ->
//如果是库工程,让他支持Maven功能
if (project.plugins.hasPlugin('com.android.library')) {
apply from: '../publishToMaven.gradle'
}
}
不通点:上面这个例子中的不通点就是作用范围不同allprojects{}>subprojects{}>project{}
经验:
通过allprojects{},subprojects{},project{}可以在多submodule的开发环境中很好使用,可以批处理部分功能,不需要多个build.gradle中都写一遍。
3.Project属性api
//Project默认属性
public interface Project extends Comparable, ExtensionAware, PluginAware {
/**
* The default project build file name.(看到这个属性API我们就知道了为什么我们project中会有一个build.gradle而不是其他名字的原因)
*/
String DEFAULT_BUILD_FILE = "build.gradle";
/**
* The hierarchy separator for project and task path names.(project使用的路径分隔符)
*/
String PATH_SEPARATOR = ":";
/**
* The default build directory name.(每个project的输出文件夹,所以我们每个project中都有一个build文件夹就是这么来的)
*/
String DEFAULT_BUILD_DIR_NAME = "build";
String GRADLE_PROPERTIES = "gradle.properties";
String SYSTEM_PROP_PREFIX = "systemProp";
String DEFAULT_VERSION = "unspecified";
String DEFAULT_STATUS = "release";
通过上面我们看到Project的默认属性是很少的,所以Google开发者给我们提供了扩展属性来使用,满足我们的需求。
图1:当前是在RootProject中定义的Project的两个扩展属性
//图1
//1.通过def定义变量
def mCompileSdkVersionABC =25
//2.通过ext加闭包来定义扩展属性
ext{
vvv='com.facebook.stetho:stetho:1.5.0'
}
图2:使用图一种RootProject中定义的vvv扩展属性
经验:
如果是多个project相互交互,使用ext{}闭包形式可以两个gradle脚本交互,但是def定义变量的形式只能当前project使用,所以建议使用ext{}闭包形式。
4.Project文件属性API
4.1获取位置相关api
//文件相关api
println "rootDir:" + getRootDir().absolutePath //获取root dir
println "buildDir:" + getBuildDir().absolutePath //获取build dir
println "projectDir:" + getProjectDir().absolutePath //获取当前Project dir
4.2:file操作api
println "file内容{:${getContent('settings.gradle')}}" //输出settings.gralde中的内容
def getContent(String filePath){
try {
def file=file(filePath)//file api参数是file的路径
return file.text //获取file中的内容
} catch (GradleException e) {
println 'file not found..'
}
return null
}
经验:
file()中的参数无需传绝对路径,而是相对于当前project的相对路径
file.text返回的结果就是文件的中内容,无需像java那样来解析文件中的内容,groovy中api帮我们做了处理。
4.3:文件拷贝Api
//file copy
copy{
from 'settings.gradle' //from文件来源
into 'settings2.gradle' //into文件目的地
// into getRootProject().getBuildDir().path+'/setting2.gradle' //也可以路径
exclude{} //排除
rename{} //重命名
}
4.4:文件树遍历Api
//file文件树遍历,遍历build/outputs文件夹下面内容,将里面的file拷贝到test目录中
fileTree('build/outputs/'){FileTree fileTree -> //参数fileTree
fileTree.visit{FileTreeElement element ->//visit遍历文件数,element是每个文件
copy{
from element.file
into getRootProject().path+'/test/'
}
}
}
5:依赖相关API
5.1buildScript 转载请注明出处:
buildscript { ScriptHandler scriptHandler ->
//配置工程的仓库地址
scriptHandler.repositories { RepositoryHandler repositories ->
repositories.jcenter()
repositories.mavenCentral()
repositories.maven {
name 'inmoc' //一般公司名字
url 'http://'
credentials {// 设置用户名密码
username = ''
password = ''
}
}
}
//配置工程的"插件"依赖地址,gradle使用的插件,外面的dependencies{compile}这些是针对应用程序的插件配置
scriptHandler.dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'
}
}
//去掉参数后
buildscript {
//配置工程的仓库地址
repositories {
rjcenter()
mavenCentral()
maven {
name 'inmoc' //一般公司名字
url 'http://'
credentials {// 设置用户名密码
username = ''
password = ''
}
}
}
//配置工程的"插件"依赖地址,gradle使用的插件,外面的dependencies{compile}这些是针对应用程序的插件配置
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'
}
}
经验:
看上面内容是不是很眼熟。之前你是否也纳闷为什么我们的项目中会有两个地方都配置了dependencies呢?那是因为gradle构建的不同需求。这两个dependencies是对两部分的依赖,一个是gradle本身需要的对第三方库的依赖,另一个是应用程序(app)中真正对第三方库的依赖。上面看到的就是针对gradle的配置依赖,android的是在app下的build.gradle中的dependencies{}闭包中,请自行查看自己的项目内容。
5.2外部命令执行
//用外部命令copy
task('apkcopy') {//task内容下章介绍
doLast { //doLast{}中的内容都是在gradle的执行阶段去执行。将outputs文件夹下的内容copy到test2中
def sourcePath = this.buildDir.path + '/outputs/'
def destaPath = this.buildDir.path + '/test2/'
def command = "mv -f ${sourcePath} ${destaPath}"
exec {//exec执行外部命令
try {
executable 'bash' //固定写法
args '-c', command
println 'command is success..'
} catch (GradleException e) {
println 'command is exception ...'
}
}
}
}
Terminal执行 ./gradlew apkcopy
经验:
这章主要说了什么是project,所有project的组织结构是一个树的形式来组织的,所以就可以用subproject在父节点中找到子节点,当然也可以找到root节点,所以构建project就是跟我们每个程序员交互的核心。project的初始化是通过我们的build.gradle,所以我们编写的脚本核心就在build.gradle中。
Ps:我是在飞~~,只有空闲时间更新博客,所以本系列偏快速入门和个人经验分享。主要讲实战中经常用到和我认为重要的内容。所以文章尽量简短,敬请谅解,希望我的博客对你有帮助!本系列暂定阅读者已经有groovy基本知识,如果需要我来说下groovy内容也可以评论中提出,后续单开一章带领大家简单入门下Groovy。
祝各位工作顺利!
有问题可联系q:1660380990邮箱[email protected]