前言
这一篇我们就开始讲解Gradle编程中很重要的一个概念Project,它就像我们Android中的Activity一样,是我们Gradle编译程序的时候每天都会见到的,下面我们就开始说说这个Project
1.深入了解Project
我们首先使用AS打开我们的实战项目HelloGradle
这里我新建了几个模块,模拟我们在真实项目中的项目结构.我们一般称这个HelloGradle为Project,而app,base,http,video我们成为module,也就是说一个Project下面可以有多个Module,而每个Module下面又可以有多个Module,但是我们一般不会这么做,所以我们在IDEA中的项目结构一般为:
+Project
--+Module
----+Module
----+Module
--+Module
--+Module
但是对于我们Gradle来说不是这样的,首先HelloGradle是一个Project,这肯定是毋庸置疑的,但是对于我们Gradle来说,像app,base等也是一个Project,我们下面就来验证下到底是不是这样的
huangfushengdeMacBook-Pro:HelloGradle huangfusheng$ ./gradlew projects
> Configure project :app
WARNING: The following project options are deprecated and have been removed:
android.enableAapt2
This property has no effect, AAPT2 is now always used.
> Configure project :base
WARNING: The following project options are deprecated and have been removed:
android.enableAapt2
This property has no effect, AAPT2 is now always used.
> Configure project :http
WARNING: The following project options are deprecated and have been removed:
android.enableAapt2
This property has no effect, AAPT2 is now always used.
> Configure project :video
WARNING: The following project options are deprecated and have been removed:
android.enableAapt2
This property has no effect, AAPT2 is now always used.
> Task :projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'HelloGradle'
+--- Project ':app'
+--- Project ':base'
+--- Project ':http'
\--- Project ':video'
To see a list of the tasks of a project, run gradlew <project-path>:tasks
For example, try running gradlew :app:tasks
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
我们执行了./gradlew projects指令,最终我们看到了结果,有一个Root Project和四个Sub Project,这就验证了我们刚才说的对于Gradle来说每个Module都是一个Project
同时我们每个Project都有一个共同点,那就是都必须有一个build.gradle,不管你是根Project还是子Project.反之如果你没有这个build.gradle,那么就不是一个Project,像我们的build,就只能称之为文件夹,而且我们的Project最终由谁去配置管理呢?就是我们这个build.gradle文件.根Project的build.gradle的作用基本上就是管理子Project,子Project的build.gradle作用就是输出,像我们这个app对应就是输出apk,像base就种就是输出aar
2.Project核心API讲解
Project中的API非常多,我们把它分成6组来学习,每组学习主要的API,这样的话效率上会很高
2.1Project相关API
2.1.1 getAllprojects()
这个方法获取工程中所有的project(包括根project与子project)
我们之前验证gradle整体结构的时候使用过一个指令./gradlew projects,输出我们当前项目的所有Project,它其实对应的就是我们getAllprojects()这个方法,我们下面就写一段代码来打印下我们项目中的Project都有哪些并且输出对应的名字
接下来我们调用下这个方法,怎么执行呢?很简单,我们随便执行一个Gradle的任务都会执行我们这个方法
注意,我们现在在build.gradle中写的代码都是在我们Gradle配置阶段执行的,那如何让我们的代码在Gradle执行阶段执行呢?我们后面学习Task的时候为大家讲解
2.1.2 getSubProjects()
这个方法时获取当前project下,所有的子project,注意:这个返回结果可能为null
我们先在根Project下面使用下这个方法,看看打印的结果
接着我们在app的build.gradle下面写上这段代码,把根project的build.gradle里面的这段代码注释掉,我们运行下,看看打印的结果
因为app下面没有子Project,所以什么都没有打印,也就是返回null
2.1.3 getParent()
获取当前project的父project(若在rooProject的build.gradle调用,则返回null)
我们现在app的build.gradle里面调用下
接着我们在根工程下的build.gradle里面调用下
这里我们发现根本就编译不过,编译器告诉我们在根工程下面调用getParent()会返回null
2.1.4 getRootProject()
取项目的根project(一定不会为null)
我们同样分两部分来调用下,一部分在app工程下,一部分在根工程下
我们先在app工程下调用下
接着我们在根工程下调用
项目的根工程只有一个,所以不管在哪打印出来的值都是一样的并且不为空
2.1.5 project(String path, Closure configureClosure)
这个方法是根据path找到对应的project,通过闭包进行配置(闭包的参数是path对应的project对象)
这个方法会通过路径path找对应的project,如果没找到就会抛出个异常,我们试验下
我们这里传入一个不存在的工程test,结果编译不过了,下面我们就来配置下我们存在的工程app (这里有个地方说下,就是如果参数的最后一个是闭包,我们是可以拿到括号外边的,这个是我们在讲groovy语法介绍过得)
我们先通过这个方法输出下我们path对应工程的名字
我们在平时开发不可能只输出个名字这么简单,那都能配置什么呢?由于我们的参数是project,所以project里面的方法都可以调用
我们先看下app的build.gradle里面的内容
这里我们看见有三样东西,那么我们在根工程下至少能为我们的app这个工程配置这三样东西
这里虽然可以这样写,但是实际我们基本上不会这样处理的,我们每个project的build.gradle最好都是只配置自己的东西,独立起来
2.1.6 allprojects(Closure configureClosure)
配置当前project和其子project的所有project
这里我们就假定所有的工程group为com.hfs,version为1.0.0-release

我们就用我们的http工程来试验下,打印它对应的group和version,看看是不是和我们写的一样
我们先看下http工程的build.gradle内容
接着我们执行下我们修改的代码:

2.1.7 subprojects(Closure configureClosure)
配置子project的所有project(不包含当前project)
我们实际开通过程中有的时候会把我们写好的组件上传到对应的maven服务器上,这个时候每个子工程可能都需要写一个上传到maven服务器的插件,这个时候我们就可以通过这个方法统一为我们的子project统一添加

2.2Task相关API
这部分我们不在这一篇进行讲解,我们会在下一篇单独写一下Task,跟我们这一篇写Project一样,这部分会在哪里进行详细讲解
2.3 属性相关API
我们首先点开Project的源码中看看它都有哪些主要的属性
首先就是默认的编译文件build.gradle,这也就是为什么我们每个工程下面都要有一个build.gradle文件;第二个就是我们Project的路径分隔符,这里不区分系统,都是:;第三个就是我们每个工程默认的输出文件夹,每个工程编译完后都会出现;下一个属性gradle.properties我们会在下面进行讲解,剩下的就都不太主要了,我们在开发中一般也不会用到
2.3.1 在gradle脚本文件中使用ext扩展属性
我们先随便看下我们某个工程的build.gradle文件,这里以video工程为例
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
buildToolsVersion "29.0.0"
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
我们在写我们应用程序的时候,我们知道在类中尽量不要使用固定的数字或者字符串的,我们称之为魔法值,我们一般是定义成常量来使用,那我们回头看我们这个build.gradle文件,如果我们也把它看成一个类的话,里面是不是有好多魔法值啊,我们就需要对他们进行统一的定义.这里就需要我们的扩展属性了,那怎么写呢?很简单,使用ext{}就可以,把你想要定义的扩展属性都写在闭包中即可,这里我们就举两个例子
我们项目中一般会有好多个工程,每个工程都需要配置这两个属性,我们难道要在每个工程的build.gradle中都写这一段代码吗?我们回想下我们上面讲的subprojects这个方法,我们是不是可以在这个方法里面为我们每个子工程配置啊,由于父project中的属性子project可以直接访问,所以我们的子工程不需要懂任何代码即可,我们实践下看看效果
我们现在根工程下配置下
然后在我们的子工程中使用下,这里我们换成http工程来验证下

编译完没发现问题,说明这么做是可以的,但是这种方式还能不能改进呢?我们想下,我们通过subprojects来为我们每个子工程都定义了扩展属性,虽然我们这里只写了一次,但是gradle在编译的时候都会为子工程定义一遍扩展属性,所以从本质上来说我们每个子工程还是定义了一个扩展属性块,只不过现在是由gradle帮我们自动进行,那应该怎么改进呢?别急,我们之前讲过子工程可以通过getRootProject这个方法来获取根工程,那么我们是不是可以直接把扩展属性定义在根工程中呢?然后在每个子工程复用这个根工程中的扩展属性,做法很简单,直接把这个扩展属性块从我们的subProjects中拿出来

我们在子工程中通过rootProject来获取相应属性

这里其实我们不加这个rootProject也可以,这里就相当于继承关系,根工程里面的属性会被子工程继承,所以子工程可以直接使用根工程的属性
到这里我们离最优的方式就差一步了,这里肯定会有同学说你为啥不一开始就说最优的呢?这个主要就演示下这个演进过程,那我们还能对它进行怎么优化呢?这里就是我们把我们的扩展属性都定义在一个gradle文件中,这样就可以减少我们根工程下的配置代码,下面我们就来看下
这里我就拿我之前写好的来展示下,具体格式都是差不多了
/**
* 全局统一配置文件
*/
ext {
//是否单独开发每个模块
isModule = false
//版本号
versions = [
applicationId : "com.hfs.latteframework", //应用ID
versionCode : 1, //版本号
versionName : "1.0.0", //版本名称
compileSdkVersion : 28,
buildToolsVersion : "28.0.3",
minSdkVersion : 19,
targetSdkVersion : 28,
androidSupportSdkVersion : "28.0.0",
constraintLayoutVersion : "1.1.1",
runnerVersion : "1.0.1",
espressoVersion : "3.0.1",
junitVersion : "4.12",
annotationsVersion : "24.0.0",
javaSDKVersion : 1.8,
multidexVersion : "1.0.2",
butterknifeVersion : "8.4.0",
arouterApiVersion : "1.4.0",
arouterCompilerVersion : "1.2.1",
arouterannotationVersion : "1.0.4",
eventbusVersion : "3.0.0",
novateVersion : "1.5.5",
loggerVersion : "2.2.0",
fastjsonVersion : "1.1.54",
immersionbarVersion : "2.3.2-beta05",
glideVersion : "4.8.0",
bannerVersion : "2.1.4",
javaxVersion : "1.2",
lombokVersion : "1.16.6",
greendaoVersion : "3.2.2",
pickerViewVersion : "4.1.6",
superAdapterVersion : "3.6.8",
scaleImageViewVersion : "3.10.0",
zxingViewVersion : "1.3",
xxpermissionsVersion : "5.5",
rx2JavaVersion : "2.1.5",
rx2AndroidVersion : "2.0.1",
photoview : "1.2.4",
smartRefreshLayout : "1.0.5.1",
baseRecyclerViewAdapterHelper: "2.9.42",
bugly : "latest.release",
kprogresshud : "1.1.0",
stetho : "1.5.0",
alertview : "1.0.3",
VerticalTabLayout : "1.2.5",
FlycoTabLayout : "2.2.1.2@aar",
leakcanary : "1.5.1"
]
//引用的依赖
dependencies = [
"appcompat_v7" : "com.android.support:appcompat-v7:${versions["androidSupportSdkVersion"]}",
"constraint_layout" : "com.android.support.constraint:constraint-layout:${versions["constraintLayoutVersion"]}",
"runner" : "com.android.support.test:runner:${versions["runnerVersion"]}",
"espresso_core" : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}",
"junit" : "junit:junit:${versions["junitVersion"]}",
"support_annotations" : "com.android.support:support-annotations:${versions["annotationsVersion"]}",
"design" : "com.android.support:design:${versions["androidSupportSdkVersion"]}",
"support-v4" : "com.android.support:support-v4:${versions["androidSupportSdkVersion"]}",
"cardview-v7" : "com.android.support:cardview-v7:${versions["androidSupportSdkVersion"]}",
"recyclerview-v7" : "com.android.support:recyclerview-v7:${versions["androidSupportSdkVersion"]}",
//方法数超过65535解决方法64K MultiDex分包方法
"multidex" : "com.android.support:multidex:${versions["multidexVersion"]}",
//路由
"arouter_api" : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}",
"arouter_compiler" : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}",
"arouter_annotation" : "com.alibaba:arouter-annotation:${versions["arouterannotationVersion"]}",
//黄油刀
"butterknife_compiler" : "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}",
"butterknife" : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",
//事件订阅
"eventbus" : "org.greenrobot:eventbus:${versions["eventbusVersion"]}",
//网络
"novate" : "com.tamic.novate:novate:${versions["novateVersion"]}",
//日志
"logger" : "com.orhanobut:logger:${versions["loggerVersion"]}",
//fastJson
"fastjson" : "com.alibaba:fastjson:${versions["fastjsonVersion"]}.android",
//沉浸式状态栏
"immersionbar" : "com.gyf.immersionbar:immersionbar:${versions["immersionbarVersion"]}",
//banner
"banner" : "com.bigkoo:ConvenientBanner:${versions["bannerVersion"]}",
//图片加载
"glide" : "com.github.bumptech.glide:glide:${versions["glideVersion"]}",
//lombok
"lombokJavax" : "javax.annotation:javax.annotation-api:${versions["javaxVersion"]}",
"lombok" : "org.projectlombok:lombok:${versions["lombokVersion"]}",
//数据库
"greenDao" : "org.greenrobot:greendao:${versions["greendaoVersion"]}",
//时间,地址,条件选择器
"pickerView" : "com.contrarywind:Android-PickerView:${versions["pickerViewVersion"]}",
//展示大图+手势滑动
"scaleImageView" : "com.davemorrissey.labs:subsampling-scale-image-view:${versions["scaleImageViewVersion"]}",
//二维码扫描
"zxing" : "com.github.0xZhangKe:QRCodeView:${versions["zxingViewVersion"]}",
//危险权限库
"xxpermissions" : "com.hjq:xxpermissions:${versions["xxpermissionsVersion"]}",
//RX家族
"rx2_java" : "io.reactivex.rxjava2:rxjava:${versions["rx2JavaVersion"]}",
"rx2_android" : "io.reactivex.rxjava2:rxandroid:${versions["rx2AndroidVersion"]}",
//图片缩放
"photoview" : "com.github.chrisbanes.photoview:library:${versions["photoview"]}",
//SmartRefreshLayout
"smartRefreshLayout" : "com.scwang.smartrefresh:SmartRefreshLayout:${versions["smartRefreshLayout"]}",
//baseRecyclerViewAdapterHelper
"baseRecyclerViewAdapterHelper": "com.github.CymChad:BaseRecyclerViewAdapterHelper:${versions["baseRecyclerViewAdapterHelper"]}",
//Bugly集成
"bugly" : "com.tencent.bugly:crashreport_upgrade:${versions["bugly"]}",
//仿ios进度条 已抽取到lib中
"kprogresshud" : "com.kaopiz:kprogresshud:${versions["kprogresshud"]}",
//安卓调试神器-Stetho
"stetho" : "com.facebook.stetho:stetho:${versions["stetho"]}",
"stetho-okhttp3" : "com.facebook.stetho:stetho-okhttp3:${versions["stetho"]}",
// 仿ios弹出对话窗体 已抽取到lib中
"alertview" : "com.bigkoo:alertview:${versions["alertview"]}",
//垂直的tabLayout
"VerticalTabLayout" : "q.rorbin:VerticalTabLayout:${versions["VerticalTabLayout"]}",
//水平tablayout
"FlycoTabLayout" : "com.flyco.tablayout:FlycoTabLayout_Lib:${versions["FlycoTabLayout"]}",
//leakcanary内存泄露
"leakcanary-android" : "com.squareup.leakcanary:leakcanary-android:${versions["leakcanary"]}",
"leakcanary-android-no-op" : "com.squareup.leakcanary:leakcanary-android-no-op:${versions["leakcanary"]}",
]
}
我们看这份定义文件,主要就是定义了两个map,一个是versions,一个是dependencies,当然你也可以按照你的定义方式多定义几个map,那我们怎么使用呢?
首先要在你的根工程的build.gradle中引入这个配置文件,一般写在最顶部
apply from: "config.gradle"
之后子工程就可以直接使用了,我这里还是拿我写的一个项目来说
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
apply plugin: 'org.greenrobot.greendao'
android {
compileSdkVersion rootProject.ext.versions.compileSdkVersion
buildToolsVersion rootProject.ext.versions.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.versions.minSdkVersion
targetSdkVersion rootProject.ext.versions.targetSdkVersion
versionCode rootProject.ext.versions.versionCode
versionName rootProject.ext.versions.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//MultiDex分包方法
multiDexEnabled true
//Arouter路由配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//防止编译的时候oom、GC
dexOptions {
javaMaxHeapSize "4g"
}
compileOptions {
targetCompatibility rootProject.ext.versions.javaSDKVersion
sourceCompatibility rootProject.ext.versions.javaSDKVersion
}
}
dependencies {
// 在项目中的libs中的所有的.jar结尾的文件,都是依赖
implementation fileTree(include: ['*.jar'], dir: 'libs')
//把implementation 用api代替,它是对外部公开的, 所有其他的module就不需要添加该依赖
api rootProject.ext.dependencies["appcompat_v7"]
api rootProject.ext.dependencies["constraint_layout"]
api rootProject.ext.dependencies["cardview-v7"]
api rootProject.ext.dependencies["recyclerview-v7"]
api rootProject.ext.dependencies["support-v4"]
api rootProject.ext.dependencies["design"]
api rootProject.ext.dependencies["support_annotations"]
api rootProject.ext.dependencies["junit"]
//MultiDex分包方法
api rootProject.ext.dependencies["multidex"]
//黄油刀
annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
api rootProject.ext.dependencies["butterknife"]
//Arouter路由
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
api rootProject.ext.dependencies["arouter_api"]
api rootProject.ext.dependencies["arouter_annotation"]
//eventbus 发布/订阅事件总线
api rootProject.ext.dependencies["eventbus"]
//日志
api rootProject.ext.dependencies["logger"]
//fastJson
api rootProject.ext.dependencies["fastjson"]
//沉浸栏
api rootProject.ext.dependencies["immersionbar"]
//banner
api rootProject.ext.dependencies["banner"]
//图片加载
api rootProject.ext.dependencies["glide"]
//lombok
api rootProject.ext.dependencies["lombok"]
api rootProject.ext.dependencies["lombokJavax"]
//时间 日期 地址 条件选中器
api rootProject.ext.dependencies["pickerView"]
//GreenDao
api rootProject.ext.dependencies["greenDao"]
//scaleImageView
api rootProject.ext.dependencies["scaleImageView"]
//二维码扫描
api rootProject.ext.dependencies["zxing"]
//危险权限
api rootProject.ext.dependencies["xxpermissions"]
//RX
api rootProject.ext.dependencies["rx2_java"]
api rootProject.ext.dependencies["rx2_android"]
//smartRefreshLayout
api rootProject.ext.dependencies["smartRefreshLayout"]
//baseRecyclerViewAdapterHelper
api rootProject.ext.dependencies["baseRecyclerViewAdapterHelper"]
}
我们这种写法呢看上去比较清晰,每个子工程也不需要进行单独的配置了,修改什么东西直接在我们这个配置文件中修改即可
2.3.2 在gradle.properties文件中使用扩展属性
这小节我们就简单说下扩展属性的另外一种方式,其实也很简单,只需要在我们gradle.properties中定义即可,从名字也可以看出这个文件就是gradle的属性
这里定义的只能是key-value方式,不能像我们上面那种方式那样,我们举个例子
我们在gradle.properties中定义一个属性isLoadHttp来标识我们是否加载http这个工程
然后我们在setting.gradle中使用下
我们先把这个属性改成true

我们看见我们这个http就变成我们项目的一个工程,接下来我们把这个属性改成false
我们看见这个http就变成了一个普通的文件夹了
我们在这文件中也可以定义我们上小节中的变量,我们就拿其中一个举例
我们在我们的video工程中使用下
我们总结下:在gradle.properties中定义的属性,可以直接访问,但得到的类型为Object,一般需要通过toXXX()方法转型。
2.4 File相关API

2.4.1 getRootDir()
获取rootProject目录

2.4.2 getBuildDir()
获取当前project的build目录,这里我们都知道我们每个project下面都会有一个build文件夹

2.4.3 getProjectDir()
获取当前project目录
我们分别在根project和video project下的build.gradle中打印下

2.4.4 File file(Object path)
以我们当前的project来寻找文件,这里我们在我们的根工程下新建一个test.gradle,里面随便写点东西方便我们一会打印

接着我们就使用file这个方法来找下这个文件,找到了我们就输出里面的内容,找不到就输出文件找不到
我们执行下这段代码

我们在试一下传入一个不存在的文件路径
直接编译失败
2.4.5 files(Object... paths)
这个方法和file类似,它的入参是一个可变参数,返回值是ConfigurableFileCollection ,就是一次可以定位多个文件,我们可以遍历这个返回值进行一系列操作
2.4.6 copy(Closure closure)
文件的拷贝,这里既可以拷贝文件也可以拷贝文件夹
我们先拷贝个文件,我们现在app工程下新建一个app.txt,我们将他拷贝到我们的根工程build文件夹下
我们运行下,看看根工程build文件夹下面有没有这个文件

接下来我们拷贝一个文件夹试试,我们就把app--outputs--logs这个文件夹拷贝到根工程的build文件夹下
我们运行下

我们还可以在闭包中指定我们不想被拷贝的文件,用exclude{}
举例:
exclude { details ->
details.file.name.endsWith('.html')
}
我们还可以对拷贝的文件进行重命名rename{}
举例:
rename { String fileName ->
fileName.replace('test', 'test1')
}
2.4.7 fileTree(Object baseDir, Closure configureClosure)
定位一个文件树(目录+文件),可对文件树进行遍历
我们现在app工程根目录下新建一个文件夹file,同时我们在这个文件夹下新建几个文件
我们就来输出下这几个文件的名字,同时将他们拷贝到根工程的build文件夹下

我们运行下


2.5 Gradle生命周期API
这部分我们在上一篇已经将讲解过了,这里也就不再讲解了 Android Gradle学习系列(五)-Gradle生命周期探索
2.6 其它API
2.6.1 依赖相关API
谈到我们的依赖相关api我们就不得不提一个方法buildscript{},我们每次新建一个项目,在项目的根工程的build.gradle中都会有这个方法,这个方法里面就是我们依赖配置的核心部分,那这里面都能配置哪些内容呢?我们看下这个方法的源码就知道了
源码中注释告诉我们闭包的参数的类型是ScriptHandler,由此我们猜想我们能在闭包中调用哪些方法由ScriptHandler这个类决定,我们继续看下ScriptHandler这个类的源码
public interface ScriptHandler {
/**
* The name of the configuration used to assemble the script classpath.
*/
String CLASSPATH_CONFIGURATION = "classpath";
/**
* Returns the file containing the source for the script, if any.
*
* @return The source file. Returns null if the script source is not a file.
*/
@Nullable
File getSourceFile();
/**
* Returns the URI for the script source, if any.
*
* @return The source URI. Returns null if the script source has no URI.
*/
@Nullable
URI getSourceURI();
/**
* Returns a handler to create repositories which are used for retrieving dependencies for the script classpath.
*
* @return the repository handler. Never returns null.
*/
RepositoryHandler getRepositories();
/**
* Configures the repositories for the script dependencies. Executes the given closure against the {@link
* RepositoryHandler} for this handler. The {@link RepositoryHandler} is passed to the closure as the closure's
* delegate.
*
* @param configureClosure the closure to use to configure the repositories.
*/
void repositories(Closure configureClosure);
/**
* Returns the dependencies of the script. The returned dependency handler instance can be used for adding new
* dependencies. For accessing already declared dependencies, the configurations can be used.
*
* @return the dependency handler. Never returns null.
* @see #getConfigurations()
*/
DependencyHandler getDependencies();
/**
* Configures the dependencies for the script. Executes the given closure against the {@link DependencyHandler} for
* this handler. The {@link DependencyHandler} is passed to the closure as the closure's delegate.
*
* @param configureClosure the closure to use to configure the dependencies.
*/
void dependencies(Closure configureClosure);
/**
* Returns the configurations of this handler. This usually contains a single configuration, called {@value
* #CLASSPATH_CONFIGURATION}.
*
* @return The configuration of this handler.
*/
ConfigurationContainer getConfigurations();
/**
* Returns the {@code ClassLoader} which contains the classpath for this script.
*
* @return The ClassLoader. Never returns null.
*/
ClassLoader getClassLoader();
}
这个源码里面的get方法我们不关心,剩下的有void repositories(Closure configureClosure);和void dependencies(Closure configureClosure);这两个方法,所以我们buildscript{}里面可以这么写了

我们首选看第一个方法repositories{},这个是配置我们工程的仓库地址,那都能配置哪些仓库呢?我们看下它的源码

同样的我们通过注释得知,决定的是我们这个RepositoryHandler类,我们看下RepositoryHandler源码
public interface RepositoryHandler extends ArtifactRepositoryContainer {
FlatDirectoryArtifactRepository flatDir(Closure configureClosure);
MavenArtifactRepository jcenter();
MavenArtifactRepository mavenCentral();
MavenArtifactRepository mavenLocal();
MavenArtifactRepository google();
MavenArtifactRepository maven(Closure closure);
IvyArtifactRepository ivy(Closure closure);
}
通过源码我们知道常用的就是上面那几种,我们分别写下
OK,接下来我们看下另外一个方法dependencies{},这里我们突出这个方法是配置我们的插件依赖地址,为啥呢?因为我们的Project中也有这个方法
也就是我们工程的build.gradle中也是可以调用一个叫dependencies名字的方法,但是和我们在buildscript方法中调用是完全不同的.我们想一下我们的gradle也是一个编程框架,那么我们在编写gradle的时候就会用到一些第三方的东西,在buildscript中dependencies就是来指定我们要依赖哪第三方的库,而我们project中build.gradle中dependencies是我们这个工程要依赖的第三方库
其实我们上面写的代码可以简化一下,

这样看起来是不是就很熟悉了,跟我们在项目中写法就完全一样了吧
我们下面说一说build.gradle中的dependencies方法,我们以我们app工程为例子
dependencies {
//第一种:依赖文件树
compile fileTree(dir: 'libs', include: ['*.jar'])
// compile file() // 依赖单个文件
// compile files() // 依赖多个文件
// 第二种:依赖仓库中的第三方库
compile 'com.android.support:appcompat-v7:26.1.0'
// 第三种依赖工程下其他Module
compile project('video') {
exclude module: 'support-v4' // 排除依赖:排除指定module
exclude group: 'com.android.support' // 排除依赖:排除指定group下所有的module
transitive false // 禁止传递依赖,默认值为false
}
// 栈内编译
provided('com.tencent.tinker:tinker-android-anno:1.9.1')
}
传递依赖:
我们一般是禁止传递依赖的,也就是工程A是禁止直接调用工程C中的依赖库的方法,只有工程B可以,因为有可能哪天工程B升级不再依赖工程C了,结果工程A还在使用,就会在编译过程中出现问题
下面我们来说说这个provided:
- 依赖包只在编译期起作用。(如:
tinker的tinker-android-anno只用于在编译期生成Application类,同时我们并不需要把该库中类打包进apk,这样可以减小apk包体积) - 被依赖的工程中已经有了相同版本的第三方库,为了避免重复引用,可以使用
provided。
2.6.2 外部命令执行
