Gradle for Android(七) 创建任务和插件

目前为止,我们已经学习了如何修改Gradle构建属性,以及如何运行任务。本章,我们会深入了解这些属性,并且创建我们自己的任务。一旦我们学会了创建任务,就可以更进一步,学习如何创建Gradle插件。

在学习创建任务之前,我们需要学习一些重要的Groovy概念。因为了解一些Groovy基础有助于我们自定义任务和插件。学习Groovy也可以帮助我们了解Gradle的原理,以及构建文件的书写格式。

本章内容有:

  • 了解Groovy
  • 学习任务
  • 接入Android插件
  • 创建插件

了解Groovy

由于多数Android开发者精通Java,所以将Groovy和Java对比学习相对要容易一些。Groovy对于Java开发者来说很容易阅读,但如果不简单了解一下的话,编写起来还是有一定难度的。

使用Groovy Console来学习Groovy是一个很好的途径。这个应用包含在Groovy SDK中,可以立即执行Groovy代码得到结果。Groovy Console也可以运行纯Java代码,可以更加方便的对比Java和Groovy代码。你可以从http://groovy-lang.org/download.html下载带有Groovy Console的Groovy SDK。

简介

Groovy源自Java,并在JVM上运行。Groovy是一种简单、直接的语言,既是脚本,也是一种成熟的编程语言。本节我们通过对比来了解Groovy的原理以及和Java的不同。

Java打印字符串代码如下:

System.out.println("Hello, world!");

而Groovy如下:

println 'Hello, world!'

两者有几个关键的不同之处:

  • 没有System.out命名空间
  • 方法的参数没有加圆括号
  • 语句末尾没有分号

示例同样在字符串的两侧使用了单引号。你也可以使用双引号,但两者作用不完全一样。双引号的字符串可以包含插值表达式。插值是计算包含占位符的字符串的过程,并将占位符替换为真实值。占位符表达式可以是变量或者方法。包含方法或者多个变量的占位符表达式需要放入{}中,并以$作为前缀;仅包含单个变量的占位符表达式只需要以$为前缀。下面是一些例子:

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."。

字符串插值也可以允许你动态执行代码,如下:

def method = 'toString'
new Date()."$method"()

如果你已经习惯了java,这看起来会很陌生,但这是动态编程语言的正常的语法和行为。

类和成员

Groovy创建类和Java很相似。下面是包含一个成员的类:

class MyGroovyClass {
    String greeting

    String getGreeting() {
        return 'Hello!'
    }
}

注意类和成员都没有明确的访问修饰符。Groovy默认的访问修饰符和Java不一样。类和方法默认是public的,成员默认是private的。

下面创建MyGroovyClass的实例:

def instance = new MyGroovyClass()
instance.setGreeting 'Hello, Groovy!'
instance.getGreeting()

你可以使用def关键字来创建新的变量。在有了类的示例之后,你就可以操作类成员了。Groovy会自动添加成员的访问器。你也可以覆写它们,比如这里我们覆写了getGreeting()方法。

你也可以直接调用成员,这实际也是在调用getter方法。也就是说你可以输入instance.greeting来代替instance.getGreeting()

println instance.getGreeting()
println instance.greeting

两种方式结果一样。

方法

和变量相似,你不需要为方法指定返回类型。你可以这么做,哪怕只是为了简洁。Groovy和Java方法的另一个不同之处是最后一行默认是返回值,即使没有使用return关键字。

为了展示Java和Groovy方法的不同,参照下面的Java方法:

public int square(int num) {
    return num * num;
}
square(2);

你需要指定方法的访问类型、返回值类型、参数类型,并在最有一行使用return关键字。

在Groovy中,这个方法如下:

def square(def num) {
    num * num
}
square 4

返回值类型和参数类型不必指明。def关键字取代了明确的类型,返回值不需要return关键字。尽管如此,为了清晰,还是推荐使用return关键字。调用方法时,你不必为它添加圆括号。

下面是Groovy更简洁的定义方法的形式:

def square = { num ->
    num * num
}
square 8

这不是一个常规的方法,而是一个闭包。Java没有类似闭包的概念。闭包在Groovy和Gradle中有非常重要的作用。

Closures(闭包)

闭包是一个可以有输入和输出的匿名代码块。它可以用来给变量赋值,也可以作为方法的参数。

你可以在花括号中添加代码块来定义简单的闭包。如果你想更明确一点,你可以将类型添加到定义中:

Closure square = {
    it * it
}
square 16

Closure显示指明了代码块的类型为闭包。前面的示例还介绍了隐式无类型参数it的概念。如果你没有为闭包显示添加参数,Groovy会自动添加一个,参数名为it,你可以在所有闭包中使用它。如果调用者没有指定任何参数,it的值为null。这使代码稍微简洁一些,但只在有单个参数时有效。

在Gradle中,我们每时每刻都在使用闭包。本书中,目前为止我们说的块就是指闭包。也就是说,android块和dependencies块都是闭包。

集合

在Gradle中使用Groovy时,有两个集合类型:list和map。

Groovy创建list非常简单。只需要:

List list = [1, 2, 3, 4, 5]

list的遍历也非常简单。可以使用each方法:

list.each() { element ->
    println element
}

你也可以使用it来精简代码:

list.each() {
    println it
}

map在Gradle的一些配置和方法中使用。map是一个键值对的集合,可以如下定义:

Map pizzaPrices = [margherita:10, pepperoni:12]

可以使用get方法或者方括号来访问map。

pizzaPrices.get('pepperoni')
pizzaPrices['pepperoni']

Groovy还有一个更加简单的方式。可以直接使用map.key的语法来得到相应的值:

pizzaPrices.pepperoni

Groovy in Gradle

现在我们已经了解了Groovy的基础,是时候回过头来重新看下Gradle的构建文件了。我们可以比较容易地理解配置语法。比如,Android插件是如何被引入构建的:

apply plugin: 'com.android.application'

这块代码使用了Groovy的简写。完全不简写的话,它是这个样子的:

project.apply([plugin: 'com.android.application'])

这样理解起来局容易多了。applyProject类的一个方法,接收一个map参数,其中键为plugin,值为com.android.application

另一个例子是dependencies块:

dependencies {
    compile 'com.google.code.gson:gson:2.3'
}

现在我们知道了这个块是一个闭包,作为Projectdependencies()方法的参数。这个闭包会传递给DependencyHandler对象,该对象有add()方法。这个方法由三个参数:一个定义配置的字符串,一个定义依赖符号的对象,一个包含这个依赖的属性的闭包。它的全写如下:

project.dependencies({
    add('compile', 'com.google.code.gson:gson:2.3', {
        // Configuration statements
    })
})

到现在为止你应该已经可以看懂这些配置文件了。

如果你想学习更多Gradle是如何使用Groovy的,你可以从看Project的官方文档看起,地址为: http://gradle.org/docs/current/javadoc/org/gradle/api/Project.html

学习任务

自定义Gradle任务可以显著提高开发者的效率。任务可以操作已有的构建过程,添加新的构建阶段,或者影响构建的输出。你可以执行简单的任务,比如重命名生成的APK。任务可以使你运行更为复杂的代码,比如在app打包前,生成几个不同密度的图像。在学会创建任务之后,你就有能力去改变构建过程的方方面面。当你学习如何使用Android插件时,这一点尤其正确。

定义任务

任务属于一个Project对象,每个任务都实现了Task接口。最简单的定义任务的方法是执行task方法,并将任务名称传递给它:

task hello

这就创建了一个任务,但是执行它不会做任何事情。想要创建一个稍微有点作用的任务,你需要为它添加动作。初学者常犯的一个错误是这样创建任务:

task hello {
    println 'Hello, world!'
}

执行的结果如下:

$ gradlew hello
Hello, world!
:hello

从结果看,你可能认为这个任务做事情了,但实际上,"Hello,world!"是在任务执行之前打印出来的。为了理解这个地方发生了什么,我们需要回过头来看下基础的东西。在第一章,我们学习了Gradle构建的生命周期:初始化阶段、配置阶段和执行阶段。在你像上例中那样添加任务时,你实际上设置了任务的配置。即使你执行其他的任务,"Hello,world!"也会打印。

如果你想在执行阶段为任务添加动作,使用如下方式:

task hello << {
    println 'Hello, world!'
}

唯一的区别是闭包前的<<符号。这会告知Gradle这段代码是为执行阶段准备的,而不是配置阶段。

为了展示它们的区别,看下面的构建文件:

task hello << {
    println 'Execution'
}

hello {
    println 'Configuration'
}

我们定义了任务hello,在执行时会打印"Execution"。我们也为hello任务的配置阶段定义了代码,即打印"Configuration"。即使配置块是在真正的任务定义代码之后定义的,它也会先被执行。上例的输出为:

$ gradlew hello
Configuration
:hello
Execution

错误的使用配置阶段导致任务失败是一个很常见的错误。在创建任务时需要牢记。

由于Groovy有很多简写,Gradle定义任务有如下几种形式:

task(hello) << {
    println 'Hello, world!'
}

task('hello') << {
    println 'Hello, world!'
}

tasks.create(name: 'hello') << {
    println 'Hello, world!'
}

前两个块是Groovy实现相同功能的两种不同方式。你可以使用圆括号,但不是必须的。你也可以不用单引号。在这两个块中,我们调用了task()方法,它需要两个参数:一个任务名称和一个闭包。task()方法是Project类的方法。

最有一个块没有使用task()方法,而是使用了Project类的tasks对象,该对象是TaskContainer的实例。这个类提供了一个create()方法,接收一个Map和一个闭包作为参数,返回一个Task

使用简写是非常便捷的书写方式,很多在线的示例和教程都是使用的简写。而全写对初次学习非常有用,这样,Gradle才看起来不那么神奇,理解起来更加容易。

任务剖析

Task接口是所有任务的基础,定义了属性和方法的集合。所有这些被DefaultTask类实现。这是一个标准的任务类型的实现,在你定义一个新的任务时,它是基于DefaultTask的。

从技术上讲,DefaultTask并不是真正的Task接口所有方法的实现类。Gradle有一个名为AbstractTask的内部类,包含所有方法的实现。因为AbstractTask是内部类,我们不能继承它。所以我们才关注继承自AbstractTaskDefaultTask类,并覆写它。

每个任务有一个Action对象的集合。在执行任务时,所有的动作会被顺序执行。你可以使用doFirst()doLast()方法添加动作。这两个方法都以闭包作为参数,然后将其封装成一个Action对象。

你只有使用doFirst()或者doLast()方法才能为任务添加在执行阶段运行的代码。我们之前使用的左移操作符<<doFirst()方法的简写。

下面是使用这两个方法的例子:

task hello {
    println 'Configuration'

    doLast {
        println 'Goodbye'
    }

    doFirst {
        println 'Hello'
    }
}

执行hello任务,输出如下:

$ gradlew hello
Configuration
:hello
Hello
Goodbye

你也可以多次调用doFirst()doLast()

task mindTheOrder {
    doFirst {
        println 'Not really first.'
    }
    doFirst {
        println 'First!'
    }
    doLast {
        println 'Not really last.'
    }
    doLast {
        println 'Last!'
    }
}

该任务的输出为:

$ gradlew mindTheOrder
:mindTheOrder
First!
Not really first.
Not really last.
Last!

注意doFirst()总是将动作加到任务的开头,doLast()总是加到末尾。你在使用这样的方法时需要谨慎,尤其是对顺序有要求时。

对于需要顺序执行的任务,你可以使用mustRunAfter()方法。这个方法允许你改变Gradle构建的依赖图。

task task1 << {
    println 'task1'
}

task task2 << {
    println 'task2'
}

task2.mustRunAfter task1

同时执行两个任务,task1总会在task2之前运行:

$ gradlew task2 task1
:task1
task1
:task2
task2

mustRunAfter()方法并没有在任务间添加依赖,你仍然可以单独执行task2,而不必执行task1。如果你需要让一个任务依赖另一个任务,可以使用dependsOn()方法。mustRunAfter()dependsOn()的区别可以从下面的示例提现:

task task1 << {
    println 'task1'
}

task task2 << {
    println 'task2'
}

task2.dependsOn task1

单独执行task2,得到的结果如下:

$ gradlew task2
:task1
task1
:task2
task2

使用mustRunAfter(),在同时执行task1、task2时,task1总是在task2之前执行,但是它们仍然可以单独执行。而使用dependsOn(),单独执行task2总会先触发执行task1。这是一个明显的不同之处。

使用任务来简化发布过程

在你将应用发布到Google Play商店之前,你需要用证书进行签名。你需要有自己的keystore,它包含一组私钥。在你有了keystore和私钥之后,可以在配置文件中定义签名配置:

android {
    signingConfigs {
        release {
            storeFile file("release.keystore")
            storePassword "password"
            keyAlias "ReleaseKey"
            keyPassword "password"
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

这种方法的缺点是,你的keystore的密码以纯文本的方式保存在仓库中。如果你是在做开源项目,这是绝对不行的;任何一个有keystore文件和私钥的人可以用你的身份发布应用。为了防止这种情况的发生,你需要创建一个任务,在每次生成发布包时,查询发布密码。虽然这有点麻烦,但它确实能使构建服务器自动生成发布版本。一个不错的保存keystore密码的解决方案是创建一个不保存在仓库中的配置文件。

在项目的根目录创建一个private.properties文件,添加如下代码:

release.password = thepassword

我们假设keystore和key本身的密码相同。如果你的密码不同,也可以添加第二个属性。

设置好这些,你可以定义一个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的文件。如果文件存在,该任务会加载所有的属性信息。properties.load()方法会查询键值对,比如我们定义的release.password

为了确保任何人在没有private.properties文件或者属性文件存在,但没有release.password属性的情况下都可以运行脚本,需要添加一个反馈。如果密码为空,在控制台要求输入密码:

if (!password?.trim()) {
    password = new String(System.console().readPassword("\nWhat's the secret password? "))
}

Groovy检查字符串是否为空是非常简单的。password?.trim()中的问号是一个null检查,在password为null时,不会调用trim()方法。我们不用进行明确的空判断,因为空的字符串在if语句中会返回false

new String()是必须的,因为System.readPassword()会返回一个字节数组,我们需要显示转换为字符串。

在有了keystore的密码之后,我们就可以为release版本配置签名配置了。

android.signingConfigs.release.storePassword = password
android.signingConfigs.release.keyPassword = password

任务写完了,我们还要确保被执行。在build.gradle文件中添加如下代码:

tasks.whenTaskAdded { theTask ->
    if (theTask.name.equals("packageRelease")) {
        theTask.dependsOn "getReleasePassword"
    }
}

这段代码会将一个闭包与Gradle和Android插件挂钩,在任务检入依赖图时,代码会被执行。packageRelease执行之前,不会要求密码,所以我们确保packageRelease依赖getReleasePassword任务。我们不可以仅仅使用packageRelease.dependsOn(),因为打包任务是Android插件动态生成的,依赖于构建变体。也就是说,在Android插件查询到所有的构建变体之前,packageRelease任务是不存在的。这个查找过程在构建之前开始。

想要这个任务工作,跟Gradle和Android插件挂钩是必须的。这是一个强有力的概念,我们会探索更多的细节。

与Android插件挂钩

在Android开发中,我们想要影响的多数任务都和Android插件相关。可以通过挂钩构建过程来增加任务的行为。上例我们学习到了在常规构建中,怎样向一个Android任务添加新的依赖任务。本节,我们将研究一些Android特有构建的钩子。

一个挂钩Android插件的方法是修改构建变体。这么做非常直接,你只需要用下面的代码片段就可以遍历所有的构建变体:

android.applicationVariants.all { variant ->
    // Do something
}

你可以使用applicationVariants对象来获取构建变体的集合。拿到构建变体的引用后,你就可以访问和更改它的属性了,比如name,description等。如果你想在Android library上使用相同的逻辑,可以使用libraryVariants对象。

注意我们遍历构建变体时,使用的是all()而不是each()方法。因为each()的触发是在evaluation阶段,在Android插件创建构建变体之前。all()方法在每次集合加入新元素时就会触发。

这个钩子可以用来在APK保存之前修改名称,或者在文件名中添加版本号。这就使维护APK变得容易,因为你不需要手动编辑文件名。下一节我们会学习如何实现它。

自动重命名APK

一个常见的修改构建构成的应用场景是在APK打包后,重命名以包含版本信息。你可以遍历应用的构建变体,修改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"))
    }
}

每个构建变体有一个输出的集合。一个Android应用的输出是一个APK。每个输出对象有一个File类型的属性outputFile。知道了输出的路径之后,你就可以修改它。本例中我们在文件名中加入了版本信息。

强大的Android插件钩子和简洁的Gradle任务的结合给了我们无限的可能。下一节,我们学习如何为应用的每个构建变体创建一个任务。

动态创建任务

基于Gradle的工作方式和任务的构造方式,在Android构建变体的基础上,我们可以在配置阶段创建新的任务。为了展示这个强大的概念,我们将创建一个任务,用来安装、运行Android应用的任何一个构建变体。install任务是Android插件定义的,但是如果你是用命令行运行installDebug任务来安装应用,安装结束后,你需要手动启动它。本节我们将创建的任务会解决最后一步的问题。

首先与applicationVariants属性挂钩:

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."
        }
    }
}

对每一个变体,检查它是否有install任务。这是必需的,因为我们要创建的新的任务需要依赖于它。确定install任务存在后,我们基于变体的名称创建一个新任务,该任务依赖于variant.install,执行时会触发install任务。task.create()的闭包中添加了描述字段,在执行gradlew tasks时会显示出来。

除了添加描述外,我们还需要添加任务执行的动作。本例中,我们想要启动应用。Android Debug Tool(ADB)提供了启动真机或者模拟器应用的命令:

$ adb shell am start -n com.package.name/com.package.name.Activity

Gradle有一个exec()的方法,可以执行命令行。为使exec()工作,我们需要加到PATH环境变量中。我们同样需要把所有的参数传递给args属性:

doFirst {
    exec {
        executable = 'adb'
        args = ['shell', 'am', 'start', '-n',"${variant.applicationId}/.MainActivity"]
    }
}

为了获取包名,可以使用构建变体的applicationId,它可能包含一个后缀(如果提供的话)。在这个例子中,后缀会有一个问题。即使我们添加了后缀,activity的路径也是不会变的,比如:

android {
    defaultConfig {
        applicationId 'com.gradleforandroid'
    }

    buildTypes {
        debug {
            applicationIdSuffix '.debug'
        }
    }
}

包名是com.gradleforandroid.debug,但是activity的路径依然是com.gradleforandroid.Activity。为了得到正确的路径,我们需要去掉后缀:

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。接下来,查找后缀。在Groovy中,字符串可以使用-做减法。这些更改确保在添加后缀的情况下,应用安装完成后可以自动运行。

创建插件

如果你有一些想要在多个工程中复用的任务,可以考虑将任务提取到自定义的插件中。这样你就可以复用构建逻辑,或者将它分享出去。

插件可以由Groovy编写,也可以基于JVM的其它语言,比如Java、Scala。实际上,Gradle Android插件的大部分是有Java和Groovy混合编写的。

创建一个简单的插件

为了提取构建文件中的构建逻辑,你可以在build.gradle文件中创建一个插件。这是最简单的自定义插件的方式。

为了创建插件,首先需要创建一个实现了Plugin接口的类。我们将使用本章已写过的动态创建run任务的代码。插件类如下:

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会作为参数传递过来,这样插件就可以配置项目或者使用项目的方法和属性。在上一个例子中,我们不能从Android插件直接调用属性,而是应该首先访问project对象。需要注意的是,这要求Android插件在我们的插件之前被引入到项目中,否则,project.android会引发异常。

任务的代码和之前是一样的,只是exec()需要用project.exec()替换。

将该插件引入到构建文件中:

apply plugin: RunPlugin

分发插件

为了分发插件并分享给其他人,你需要将它移动到一个单独的模块(或项目)中。单独的插件有它自己的构建文件来配置依赖和分发的方法。这个模块生成一个JAR文件,包含插件类和属性。你可以使用这个JAR文件将插件应用到模块或者项目中,也可以分享给他人。

和其他Gradle项目一样,创建一个build.gradle来配置构建:

apply plugin: 'groovy'

dependencies {
    compile gradleApi()
    compile localGroovy()
}

由于我们是用Groovy写的插件,所以需要引入Groovy插件。Groovy插件继承自Java插件,使我们可以构建和打包Groovy类。Groovy同样支持Java,你可以混合使用。你甚至可以使用Groovy扩展Java类。这使Groovy很容易上手。

gradleApi()依赖用于在我们的插件中访问Gradle的命名空间。
localGroovy()依赖是Groovy SDK在Gradle中安装的发行版。
这两个依赖是Gradle自动添加的。

如果你计划公开发布你的插件,请确保在构建文件中定义了group和version信息,例如
group = 'com.gradleforandroid'
version = '1.0'

为了使用独立模块的代码,我们还需要确保正确的目录结构:

plugin
└── src
     └── main
         ├── groovy
         │    └── com
         │        └── package
         │             └── name
         └── resources
              └── META-INF
                  └── gradle-plugins

和其他的Gradle模块一样,我们需要提供一个src/main目录。因为这是一个Groovy工程,所以main的子目录是groovy而不是java。main的另一个子目录是resources,用来指定插件的属性。

我们在包目录创建了一个RunPlugin.groovy的类文件:

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能够找到插件,我们需要提供一个属性文件。将属性文件添加到src/main/resources/META-INF/gradle-plugins/目录。文件名需要匹配插件的ID。对于RunPlugin来说,属性文件名称为com.gradleforandroid.run.properties,它的内容是:

implementation-class=com.gradleforandroid.RunPlugin

属性文件内容仅仅是实现了Plugin接口的类的包名和类名。

准备好接口和属性文件后,我们可以使用gradlew assemble命令来构建插件,这会在构建输出目录创建一个JAR文件。如果你想将插件上传到Maven仓库,你首先需要引入Maven插件:

apply plugin: 'maven'

然后,你需要配置uploadArchives任务:

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('repository_url'))
        }
    }
}

uploadArchives是预定义的任务。为这个任务配置了仓库之后,你就可以运行它来发布你的插件。本书不会讲述如何设置Maven仓库。如果你想让你的插件公开可用,可以发布到Gradleware的插件门户(https://plugins.gradle.org)。插件门户有一大批Gradle插件(不仅仅是Android的),如果你想扩展Gradle的默认行为,可以去浏览一下。你在https://plugins.gradle.org/docs/submit网站可以学习如何发布插件。

为自定义插件写测试不是本书的内容,如果你想公开发布插件,强烈建议进行测试。你可以在Gradle用户指南https://gradle.org/docs/current/userguide/custom_plugins.html#N16CE1学习编写测试

使用自定义插件

要使用插件,需要在buildscript块将其添加为依赖。首先,我们需要配置一个新仓库。仓库的配置依赖于插件发布的途径。其次,我们需要在dependencies块配置插件的类路径。

如果你想引入上例创建的JAR文件,可以定义一个flatDir仓库:

buildscript {
    repositories {
        flatDir { dirs 'build_libs' }
    }

    dependencies {
        classpath 'com.gradleforandroid:plugin'
    }
}

如果已经将插件上传到Maven或者Ivy仓库,配置会有所不同。我们在第3章讲了配置管理,所以我们将不再讲述不同之处。

配置好之后,我们需要应用插件:

apply plugin: com.gradleforandroid.RunPlugin

在使用apply()方法的时候,Gradle会创建一个插件类的实例,并执行插件自己的apply()方法。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,501评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 1.介绍 如果你正在查阅build.gradle文件的所有可选项,请点击这里进行查阅:DSL参考 1.1新构建系统...
    Chuckiefan阅读 12,118评论 8 72
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,748评论 6 342
  • 一别便是一生
    玉梅_901d阅读 97评论 0 0