介绍
到目前为止,我们已经看到了很多Gradle构建的属性,并且知道了怎么去执行Tasks。这一章,会更多的了解这些属性,并且创建我们自己的Task。一旦知道如何自定义Task之后,就可以完成更多的事情,并且自定义自己的插件,而在多工程中使用这些Task和Plugin。
之前我们看到了如何创建自定义Task,并且了解了一些Groovy脚本。知道Groovy也帮我们理解Gradle如何工作,并且为什么构建配置文件可以这样配置。
这一章会从下面的角度来介绍:
- Understanding Groovy
- Getting started with tasks
- Hooking into the Android plugin
- Creating your own plugins
Understanding Groovy
Groovy对于Java开发者而言非常容易阅读,但是如果没有一个简单的介绍的话,Groovy代码也是一个比较难的任务。
Groovy基于Java并且在JVM中执行。它的宗旨是变得更简单,更直接的语言,就像脚本语言一样。而我们将Grovvy和Java对比,可以让我们更好的了解Groovy如何工作的,并且更清楚的了解到这两种语言的区别。
在Java中打印一个字符串如下:
代码语言:javascript复制System.out.println("Hello, world!");
而在Groovy中如下:
代码语言:javascript复制println 'Hello, world!'
我们可以立即发现一些关键的区别:
- 没有System.out的命名空间
- 没有参数路径
- 结尾没有分号
示例中使用单引号包围着一个String。你也可以使用单引号或者双引号,但是他们是有区别的。双引号的String可以包含一些差值表达式。差值表达式可以值或者函数来代替其中的占位符。而占位符表达式会包含多个值,并且通过$前缀来代表值。例如:
代码语言:javascript复制 def name = 'Andy'
def greeting = "Hello, $name!"
def name_size "Your name is ${name.size()} characters long."
greeting
值代表着Hello,Andy
字符串。并且name_size
的值为Your name is 4 characters long
。
字符串差值器允许我们执行动态代码,比如说下面的代码是打印正确的日期:
代码语言:javascript复制def method = 'toString'
new Date()."$method"()
这在Java中看起来很奇怪,但是在动态语言的里面确实很平常的。
Classes and members
在Groovy中创建Class如下,包含一个成员和一个函数:
代码语言:javascript复制class MyGroovyClass {
String greeting
String getGreeting() {
return 'Hello!'
}
}
注意到成员和函数都没有类似于private ,public的访问权限。而默认的访问权限和Java不同,Groovy中的类都是Public的,就和Method一样,但是成员变量却是私有的。
如果要创建一个MyGroovyClass
变量,如下:
def instance = new MyGroovyClass()
instance.setGreeting 'Hello, Groovy!'
instance.getGreeting()
我们可以使用def
关键字来创建一个新的变量。一旦创建出了一个变量,就可以操作它的成员了。Groovy自动添加了访问权限,你也可以重写他们。就像我们定义了getGreeting
在MyGroovyClass
中。如果没有指定的话,可以使用setter和getter方法来访问成员变量。
如果你尝试直接调用一个成员,那么需要调用getter方法即可。也就是说,你不需要定义instance.getGreeting()
函数,你可以直接调用instance.geeting
即可。
println instance.getGreeting()
println instance.greeting
上面两行代码完成了相同的事情。
Methods
就像变量一样,我们不需要指定具体的返回类型给Method。虽然为了比较清晰的能够看清楚函数的结构,我们也会定义好返回值。另外一个不同的地方就是,Groovy默认会有返回值,而不需要使用return
关键字。
例如Java中返回一个值的平方:
代码语言:javascript复制public int square(int num) {
return num * num;
}
square(2);
你需要指定函数为public,并且返回的类型,参数,以及返回对应类型的值。同样的函数定义在Groovy中如下:
代码语言:javascript复制def square(def num) {
num * num
}
square 4
没有返回类型,没有参数类型。通过使用def
关键字来代替一个具体的类型,并且返回具体的值也没有通过return返回。当调用这个函数的时候,也不需要括号和分号。另外一种Groovy的定义方式如下:
def square = { num ->
num * num
}
square 8
这不是一个常规的方法,而是一个闭包。闭包的概念和Java中不一样,但是在Groovy和Gradle中尤为重要。
Closures
闭包是匿名的代码块,能够接受参数并且返回一个值。它能够被分配给变量,也能够作为参数传递给函数。
你可以定义一个简单的闭包,在花括号中添加代码块即可。如果你希望它能够更直接一些,那么可以在定义中添加类型,例如:
代码语言:javascript复制Closure square = {
it * it
}
square 16
通过添加Closure
定义让每个人都知道这段代码是闭包。如果你不想在闭包中指定参数具体的类型,Groovy会自动添加一个。这个参数的名字就叫做it
。如果调用者没有指定任何参数,那么这个参数就会是null。这可以使代码更加简洁,但仅当闭包只用一个参数时才有用。
在Gradle的上下文中,我们总是使用闭包。例如,android
代码块以及dependencies
代码块都是闭包。
Collections
Gradle中有两个比较重要的概念,List和Map。 在Groovy中创建List很简单,不需要特殊的初始化:
代码语言:javascript复制List list = [1, 2, 3, 4, 5]
列表的迭代器也很简单。你可以通过each
方法来遍历每个元素:
list.each() { element ->
println element
}
而each
函数可以让你访问List中的每个元素。而我们也可以通过it
变量更方便的调用:
list.each() {
println it
}
另外一种类型就是Map。Map通常用在Gradle的设置和函数中。Map中保存着K-V的列表。我们可以通过以下方式定义Map:
代码语言:javascript复制Map pizzaPrices = [margherita:10, pepperoni:12]
如果要访问Map中的某一条,则使用get方法或者单引号访问即可:
代码语言:javascript复制 pizzaPrices.get('pepperoni')
pizzaPrices['pepperoni']
Groovy也为该功能提供了一个更简便的方法,你可以通过.的方式来访问某个值:
代码语言:javascript复制pizzaPrices.pepperoni
Groovy in Gradle
打开一个Gradle的build.gradle
文件,看整个构建中的Android Plugin应用的地方:
apply plugin: 'com.android.application'
这段代码是Groovy精简版,如果原版的Groovy代码应该是:
代码语言:javascript复制project.apply([plugin: 'com.android.application'])
重写Groovy的精简的方法,我们调用了Project
类的``apply方法。而
apply方法只有一个参数,而该参数是一个Map,里面包含了Key为
plugin,Value为
com.android.application```。
另外一个例子,就是dependencies
代码块,之前我们定义dependencies
如下:
dependencies {
compile 'com.google.code.gson:gson:2.3'
}
我们现在知道这个代码块是一个闭包,调用了Project
对象的dependencies
函数。这个闭包传入的是一个DependencyHandler
对象,而这个对象中存在add
函数。
这个函数接受了三个参数,一个String定义了配置,一个对象定义了依赖库,以及一个闭包可以指定依赖的属性。全部展开如下:
project.dependencies({
add('compile', 'com.google.code.gson:gson:2.3', {
// Configuration statements
})
})
如果希望了解更多的Groovy在Gradle中的内幕,最开始可以看看
Project
的官方文档。地址为:http://gradle.org/docs/current/javadoc/org/gradle/api/Project.html
Getting started with tasks
自定义Gradle任务可以提升我们的开发效率。Tasks可以操作已存在的构建流程,添加新的构建步骤,并且影响构建的输出。我们可以执行一些简单的任务,比如说可以通过Hook Gradle的Android Plugin重命名一个已经生成的APK。Tasks也允许你执行更多复杂的代码,以至于我们可以在APK打包前生成多Density的图片。例如,一旦你知道如何创建自定义Tasks了,你就会发现你可以改变构件流程了。
Defining tasks
Tasks属于Project对象,并且每个Task实现了Task
接口。定义一个Task最简单的方法就是使用Tasks的名字作为参数执行Task方法即可。例如:
task hello
这将创建出Task。但是不会做任何事情,如果我们希望添加一些事件,则可以通过以下方式:
代码语言:javascript复制task hello {
println 'Hello, world!'
}
当执行这个任务的时候,就会发现:
代码语言:javascript复制$ gradlew hello
Hello, world!
:hello
从这个输出,可以看出:Hello,world!
在任务执行前被打印出来了。回顾一下之前说的Gradle构建流程,有三个阶段:初始化阶段,配置阶段,执行阶段。当按照上述例子添加Task时候,实际上是配置了这个Task。甚至如果你执行其他的任务,Hello,World!
这条消息仍然会出现。
如果你希望在执行阶段添加一些事件的话,则可以使用:
代码语言:javascript复制task hello << {
println 'Hello, world!'
}
唯一不同的是在闭包前加入了<<
。这告诉了Gradle代码是在执行阶段,而不是在配置阶段。
为了证明这个区别,我们可以在build.gradle
中加入:
task hello << {
println 'Execution'
}
hello {
println 'Configuration'
}
我们定义了一个当它执行的时候会打印的Task。我们也定义了一个在Configuration阶段打印的的Task。即使它在真正的Task之后定义的,也会首先执行。输出的结果如下:
代码语言:javascript复制$ gradlew hello
Configuration
:hello
Execution
由于Groovy有很多简洁定义的方式,以下为一些示例:
代码语言:javascript复制task(hello) << {
println 'Hello, world!'
}
task('hello') << {
println 'Hello, world!'
}
tasks.create(name: 'hello') << {
println 'Hello, world!'
}
第一个和第二个代码块通过两种不同的方式实现同一个效果。你可以使用单引号,也可以使用括号。在这两个代码块中,我们调用的是task()
函数,它会有两个参数,一个是Task的名字,另外一个是一个闭包。task()
函数就是Gradle中Project
类中的一部分。
最后一个代码块则不是使用task()
函数。它用的是一个名为tasks
的对象,而这个对象则是TaskContainer
的实例。并且,这个实例代表着每一个Project。它提供了create
函数,而这个函数会通过一个Map对象和一个闭包作为参数,并且返回一个Task对象。
Anatomy of a task
Task
接口是所有Task,以及定义一系列Properties和Methods的基础。所有的这些都被一个默认的Class实现了,它的名字叫做DefaultTask
。这是标准的Task类型的实现,当创建一个新的Task的时候,它会基于DefaultTask
。
每个Task都包含了一系列Action
对象。当Task被执行的时候,这些Action都会按照顺序执行。我们可以使用doFirst
和doLast
函数来添加Action。这些方法都添加一个闭包作为参数,并且把他们包装到一个Action对象中。
你只需要通过doFirst()
和doLast()
来在Execution阶段来执行代码。而<<符号则其实代表着在doFirst
中定义了Action。举例如下:
task hello {
println 'Configuration'
doLast {
println 'Goodbye'
}
doFirst {
println 'Hello'
}
}
当我们执行hello
这个任务时,则会打印出:
$ gradlew hello
Configuration
:hello
Hello
Goodbye
即使打印Goodbye
那行代码定义在Hello
之前,它也会在Task执行的时候,按照正确的位置打印出来。你也可以多次使用doFirst()
和doLast()
,例如:
task mindTheOrder {
doFirst {
println 'Not really first.'
}
doFirst {
println 'First!'
}
doLast {
println 'Not really last.'
}
doLast {
println 'Last!'
}
}
执行完这个任务,就会打印出:
代码语言:javascript复制$ gradlew mindTheOrder
:mindTheOrder
First!
Not really first.
Not really last.
Last!
注意,doFirst()
函数会加在Task的Action集合最开始的地方,而doLast()
添加的Action则在最后的位置。这也就意味着,我们使用这些函数的时候需要很小心,尤其注意它的顺序。
如果它依赖于某个顺序执行的任务的话,那么可以使用mustRunAfter()
函数。这个函数允许你影响Gradle构建的Dependency的DAG。当你使用mustRunAfter
时,需要指定两个任务,其中一个必须在另外一个之前执行:
task task1 << {
println 'task1'
}
task task2 << {
println 'task2'
}
task2.mustRunAfter task1
执行task1和task2将会得到task1在task2之前执行,而忽略你所指定顺序。
代码语言:javascript复制$ gradlew task2 task1
:task1
task1
:task2
task2
mustRunAfter()
函数不会在两个Task之间添加依赖关系。它可以在Task1不执行的情况下,仍然可以执行Task2。如果你希望添加两个Task之间的依赖关系的话,那么需要使用dependsOn()
。例如:
task task1 << {
println 'task1'
}
task task2 << {
println 'task2'
}
task2.dependsOn task1
而当你只执行task2,而不执行task1的时候:
代码语言:javascript复制$ gradlew task2
:task1
task1
:task2
task2
使用mustRunAfter()
的时候,同时执行task2和task1,并且在task2优先执行的时候,他们还是会有执行的依赖关系。而dependsOn()
的话,task2必须和task1挂钩,即使没有明确的说明。这是一个很重要的点。
Using a task to simplify the release process
在发布App之前,你需要对APK进行签名。而签名前,需要创建自己的keystore
,其中包含了很多private keys。当你创建完keystore
后,你可以在Gradle中定义签名的配置了。例如:
android {
signingConfigs {
release {
storeFile file("release.keystore")
storePassword "password"
keyAlias "ReleaseKey"
keyPassword "password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
这种方式的缺点就是密码会被铭文保存在仓库中。如果你正在为开源做奋斗的话,那不要用这种方式。任何一个拥有keystore文件和password的人都可以使用你的ID发布App。
为了避免这种情况,你可以创建一个Task,在每次打Release包的时候询问Release的Password。这会有一点麻烦,而且在自动持续集成构建Release包的情况下也是不可能的。一种比较好的解决方案就是,创建一个配置文件保存keystore的密码,而这个配置文件不在仓库中。
我们可以在根目录下提供一个名为private.properties
的文件,并且添加:
release.password = thepassword
我们假设Keystore和key的密码相同。如果你有两个不同的密码,那么则可以创建第二个属性。一旦设置完成,你可以定义一个新的Task,名为getReleasePassword
:
task getReleasePassword << {
def password = ''
if (rootProject.file('private.properties').exists()) {
Properties properties = new Properties();
properties.load(rootProject.file('private.properties').newDataInputStream())
password = properties.getProperty('release.password')
}
}
这个任务会在根目录寻找一个名为private.properties
的文件。如果文件存在,那么Task会加载所有的properties。并且properties.load()
函数会查找Key-Value对,就像我们在properties文件中定义的release.password一样。
为了保证没有private properties
文件的人也可以运行这个脚本,或者处理如果文件存在,但是password属性不存在的情况,我们可以添加一个fallback。如果password仍然为空,那么可以在console中询问Password:
if (!password?.trim()) {
password = new String(System.console().readPassword ("nWhat's the secret password? "))
}
在Groovy中检查字符串是否为空是一个很简单的操作。用?
标志的password?.trim()
检查了password是否为null,并且使用trim()
避免password为空。
使用new String
是必须的,因为System.readPassword()
会返回一个字符数组,然后通过String来转换成字符串。一旦我们拥有了keystore的密码,我们就可以在release构建中配置签名:
android.signingConfigs.release.storePassword = password
android.signingConfigs.release.keyPassword = password
现在我们已经完成我们的任务,我们需要确认当执行一次Release构建的时候是否成功,接下来在build.gradle
中添加这几行:
tasks.whenTaskAdded { theTask ->
if (theTask.name.equals("packageRelease")) {
theTask.dependsOn "getReleasePassword"
}
}
这段代码Hook进了Gradle,并且在运行的时候Android Plugin会把闭包加入到Dependency Graph中。直到packageRelease
执行之前,password都不是必须的,所以我们需要确保packageRelease
任务依赖于我们的getReleasePassword
任务。我们不能直接使用packageRelease.dependsOn()
的原因是Android Plugin会基于Build Variant动态的生成打包的Tasks。这也就意味着,packageRelease
任务直到Android Plugin扫描完所有的Build Variants之前,都不会存在。而发现的过程在Build之前就已经开始了。
在添加了Task的构建Hook之后,执行gradlew assembleRelease
任务的结果如下:
Hook Android Plugin
就像上面截图所示,private.properties
文件不可用,所以task在console中询问password。这种情况下,我们需要添加一些提醒,如何创建properties
文件,并且添加password属性让未来的构建更贱简单。一旦我们的Task选择了keystore的密码,Gradle就可以开始打包我们的APP并且完成构建了。
为了让这个Task可以正常运转,它本质就是Hook到Gradle和Android Plugin中。
Hooking into the Android plugin
当开发Android App的时候,我们希望修改的任务大多都是与Android Plugin相关的。之前的例子,我们可以看到如何在一个自定义的Task中添加依赖。在这一届,我们来看看如何进行Android特殊的构建Hook。
一种Hook到Android Plugin的方法是操作Build Varian。我们只需要在遍历Variant的时候,完成我们的任务即可。
代码语言:javascript复制android.applicationVariants.all { variant ->
// Do something
}
为了得到所有的Build Variants,我们可以使用applicationVariants
对象。一旦我们引用到了一个具体的Build Variant,我们就可以访问它的属性,并且操作它的属性,比如说名字,描述等等。如果你希望在Android Library中加入相同的逻辑,那么使用libraryVariants
来替代applicationVariants
即可。
这种Hook可以用来修改APK的名字,并且在文件名后添加版本号。这样可以更简单的生成一个带版本的APK名,而不需要手动修改文件名。接下来则看看如何实现
Automatically renaming APKs
在打包完后,我们来重命名APK。我们可以遍历App的Build Variants,并且修改outputFile
属性。如下代码所示:
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
output.outputFile = new File(file.parent,file.name.replace(".apk", "-${variant.versionName}.apk"))
}
}
每个Build Variant的输出都是一个APK文件。variant.outputs对象都会有一个属性名为outputFile
,而它则是File
类型的。一旦我们知道了output的路径后,我们就可以操纵它了。
如上所示,我们在文件名中添加了版本号,而APK的名字也会从app-debug.apk
修改为app-debug-1.0.apk
。接下来,我们来看看如何为每一个Build Variant创建一个Task。
Dynamically creating new tasks
由于Gradle工作方式以及Tasks的构建,我们可以在Configuration阶段基于Build Variant创建我们自己的Task。
为了解释这个强大的概念,我们会创建一个Task,但不是安装,而是运行Android App的某一个Build Variant。Install Task只是Android Plugin中的一部分,但是如果你通过命令行的installDebug
任务安装了Apk的话,当安装完成后,需要手动启动App才行。而我们创建的这个Task则会把最后一步去掉。
通过Hook Application Variant中的属性:
代码语言:javascript复制android.applicationVariants.all { variant ->
if (variant.install) {
tasks.create(name: "run${variant.name.capitalize()}",
dependsOn: variant.install) {
description "Installs the ${variant.description} and runs the main launcher activity."
}
}
}
对于每个Variant,我们检查它是否有install这个任务。因为我们需要依赖install
任务,所以必须要检查这个任务是否存在。一旦我们确定了install任务存在,我们就可以创建一个新的Task,并且基于Variant的名字赋予这个Task名字。我们需要将我们新建的任务依赖variant.install
。这会在我们的任务执行前打开install任务。而在tasks.create()
的闭包中,我们添加了一个description,可以帮助我们在执行gradlew tasks
的时候展示日志。
在添加完description之后,我们也会添加真正的Task Action。在这个例子中,我们希望启动APP。你可以通过Android Debug Tool(ADB)在已经连接的设备或者模拟器中启动APP。
代码语言:javascript复制$ adb shell am start -n com.package.name/com.package.name.Activity
Gradle有一个函数叫做exec()
,这个函数可以让我们在命令行执行命令。为了确保exec()
可以正常工作,我们需要提供一个可执行的环境变量。我们也需要传递一些参数,例如:
doFirst {
exec {
executable = 'adb'
args = ['shell', 'am', 'start', '-n',"${variant.applicationId}/.MainActivity"]
}
}
为了得到包名,我们使用Build Variant中带有后缀的Application ID。而如果我们加了后缀,Activity的classpath仍然相同。例如:
代码语言:javascript复制android {
defaultConfig {
applicationId 'com.gradleforandroid'
}
buildTypes {
debug {
applicationIdSuffix '.debug'
}
}
}
包名为com.gradleforandroid.debug
,但是Activity的路径还是com.gradleforandroid.Activity
。为了保证我们得到正确的Activity类,我们从ApplicationId中带入后缀:
doFirst {
def classpath = variant.applicationId
if(variant.buildType.applicationIdSuffix) {
classpath -= "${variant.buildType.applicationIdSuffix}"
}
def launchClass = "${variant.applicationId}/${classpath}.MainActivity"
exec {
executable = 'adb'
args = ['shell', 'am', 'start', '-n', launchClass]
}
}
首先,我们创建了一个变量为classpath
,该值为applicationId。然后我们通过buildType
找到后缀。在Groovy中,我们可以通过-=
运算符来从String中减去一个String。这些修改可以保证在安装过后,使用后缀的APP也不会打开失败。
Creating your own plugins
如果你有一系列的Gradle的Tasks希望在多个Project中重用,那我们可以考虑把这些Task添加到一个自定义的插件中去。这样可以让我们自己的构建逻辑与别人共享。
Plugin也可以使用Groovy编写,Java或者Scala也都可以,只要是基于JVM的语言都可以。实际上,大部分的Android Plugin都是Java与Groovy混编的。
Creating a simple plugin
为了从已经保存到build.gradle
中的构建逻辑提取出来,我们可以在build.gradle
中创建一个Plugin。这是最简单的方法。
为了创建一个Plugin,我们需要创建一个新的Class,实现Plugin
接口。我们也将使用我们之前动态创建Tasks的代码。我们的Plugin类如下:
class RunPlugin implements Plugin<Project> {
void apply(Project project) {
project.android.applicationVariants.all { variant -> if (variant.install) {
project.tasks.create(name: "run${variant.name.capitalize()}", dependsOn: variant.install) {
// Task definition
}
}
}}
}
Plugin
接口定义了apply()
函数。Gradle会在插件使用的时候,调用这个函数。Project
对象会作为参数传递,并且可以在Plugin中配置该project对象,并且使用它的函数以及属性。
在之前的例子中,我们需要首先需要访问project
对象,需要注意我们需要在build.gradle
中Apply这个Plugin才行,否则会导致异常。
为了保证这个Plugin在我们的构建配置中被Apply,需要在build.gradle
中添加以下这一行:
apply plugin: RunPlugin
Distributing plugins
为了发布一个Plugin,我们需要把它移动到一个单独的Module或者Project中。一个单独的Plugin拥有它自己的build.gradle
文件来配置dependencies。这个Module会产生一个Jar文件,包括包含了Plugin的classes和属性。我们可以使用这个JAR文件将插件应用到多个模块和项目中,并与其他模块共享。而Gradle工程,则需要创建一个build.gradle
文件进行配置:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
一旦使用Groovy写Plugin后,我们就需要应用groovy
这个插件。Groovy Plugin集成自Java Plugin,并且能让我们构建以及打包Groovy的类。Groovy和Java都可以支持,所以我们可以混编。你甚至可以使用Groovy来继承一个Java类。甚至你都感觉不到在使用Groovy。
为了开始我们单独模块的代码,我们首先需要保证正确的目录结构:
代码语言:javascript复制plugin
└── src
└── main
├── groovy
│ └─com
│ └─package
│ └── RunPlugin.groovy
└── resources
└── META-INF
└── gradle-plugins
对于任意的Gradle模块,我们需要提供一个src/main
目录。因为这是Groovy工程,main的子目录使用groovy来替代java。而另外一个子目录叫做resources
,用来指定我们Plugin的属性。我们创建了一个文件名为:RunPlugin.groovy
在``package```目录下,而这个目录下我们会定义我们Plugin的类:
package com.gradleforandroid
import org.gradle.api.Project
import org.gradle.api.Plugin
class RunPlugin implements Plugin<Project> {
void apply(Project project) {
project.android.applicationVariants.all { variant ->
// Task code
}
}
}
为了Gradle能够查找到这个Plugin,我们需要提供一个properties
的文件。并且将该文件放到src/main/resources/META-INF/gradle-plugins/
这个目录下。这个文件的名字需要匹配Plugin的ID。例如:RunPlugin
,这个文件名称就叫做com.gradleforandroid.run.properties
,该文件的内容为:
implementation-class=com.gradleforandroid.RunPlugin
这个properties
文件中唯一的东西就是包名以及Plugin具体实现的类名。当Plugin和Properties文件准备完成,我们就可以通过gradlew assemble
命令来构建Plugin了。这会在构建的output
目录下创建一个Jar文件。如果你希望把这个插件发布到Maven仓库上的话,你需要应用Maven Plugin
apply plugin: 'maven'
然后配置uploadArchives
任务:
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('repository_url'))
}
}
}
uploadArchives
是一个已经定义过的Task。一旦你配置了任务中的仓库,你就可以执行它发布你的Plugin。
Using a custom plugin
为了使用一个Plugin,我们需要在buildscript中添加它作为dependency。首先,我们需要配置一个新的repository
。这个配置决定了Plugin如何被共享。然后,我们需要在dependencies
中配置Plugin的classpath。如果你想包含一个Jar文件的话,我们可以定义flatDir
仓库:
buildscript {
repositories {
flatDir { dirs 'build_libs' }
}
dependencies {
classpath 'com.gradleforandroid:plugin'
}
}
如果我们已经在Maven或者Ivy仓库中上传了该插件的话,那么它就会有一点不一样。在我们设置了dependency之后,我们就可以应用该Plugin了:
代码语言:javascript复制apply plugin: com.gradleforandroid.RunPlugin
当使用了apply()
方法,Gradle会创建一个Plugin的实例,然后执行Plugin的apply()
方法。