An overview of the

I started a new series. The goal of this series of learning Gradle is to thoroughly understand Gradle. The main goal is to make notes on your own understanding to prevent forgetting

Gradle Series (1) : Groovy learning

Gradle Learning series (2) : Gradle core decryption

Gradle Plugins

Introduction to the

Gradle itself only provides the basic core functionality, other features such as the ability to compile Java source code, the ability to compile Android projects and so on are implemented through plug-ins. To apply a plug-in, you need to apply the plug-in to your Project, which is done through the project.apply () method. Gradle generally has two types of plug-ins, called script plug-ins and object plug-ins.

A script plugin is an extra build script that further configures the build and can be thought of as a normal build.gradle.

Object plug-ins, also known as binary plug-ins, are classes that implement the Plugin interface. Here’s how to use them.

Scripting plug-ins

For example, we create a utils. Gradle at the root of the project

def getxmlpackage(boolean x){
    def file=new File(project.getProjectDir().getPath()+"/src/main/AndroidManifest.xml");
    def paser = new XmlParser().parse(file)
    return paser.@package
}

ext{
    getpackage = this.&getxmlpackage
}
Copy the code

This is a simple script plug-in, and then reference the script plug-in in app Moudle

apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"
Copy the code

Then you can call the method in the script plug-in

Object plug-in

Object plug-ins are plug-ins that implement the org.gradle.api.plugins interface. Object plug-ins can be classified into internal plug-ins and third-party plug-ins.

Inside the plugin

Gradle contains a large number of plugins, such as Java plugins and C plugins that we often use, which we can directly introduce

apply plugin:'java'
apply plugin:'cpp'
Copy the code

Tripartite plug-in

Third party object plug-ins are usually JAR files that need to be set up using BuildScrip to make the build script aware of the existence of the third party plug-ins. Define the original repository and dependencies of the plug-in in BuildScrip, then configure the apply method. Android Gradle plugin is also a third party plugin. If we want to introduce Android Gradle plugin, we can write:

buildscript {
    repositories {
    	// Configure the repository
        google()
        jcenter()
    }
    dependencies {
    	// Configure plug-in dependencies
        classpath 'com. Android. Tools. Build: gradle: 3.5.3'}}// Then you can introduce android plugins where needed
apply plugin: 'com.android.application'
Copy the code

Custom object plug-ins

Customizing a Plugin is to implement org.gradle.api.Plugin

Build script

This is a way to write the plugin code directly in the build script, but it can only be used in this file. For example, I added the following code directly in the app’s build.gradle

class myPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        println("MyPlugin executes.")

        project.task("myTask"){
            doLast {
                println("MyTask executed.")}}}}Copy the code

Then introduce the plug-in defined in this file

apply plugin: myPlugin
Copy the code

Gradlew MyTask (MyTask) /gradlew MyTask (MyTask

> Task :mylibrary2:MyTask myTask executes the execution phase, task':mylibrary2:myTask'Time:1ms
Copy the code

Generally, we do not use plug-ins implemented in this way, because this way is too limited and can only be used in this Project, while other projects cannot be used

BuildSrc project

BuildSrc is Gradle’s default plugin directory. When building Gradle, this directory is automatically recognized and the code in it is compiled into a plugin.

  • First create a Java Module named buildSrc, then save build.gradle and SRC /main, delete everything else, make sure the name is buildSrc, otherwise you won’t find the plugin

  • Then modify the contents of Gradle

apply plugin: 'groovy'  / / must
apply plugin: 'maven'

dependencies {
    implementation gradleApi() / / must
    implementation localGroovy() / / must
    // If you want to use the Android API, you need to reference this, which will be used to implement the Transform
    / / implementation 'com. Android. View the build: gradle: 3.3.0'
}

repositories {
    google()
    jcenter()
    mavenCentral() / / must
}

// Set the project entry to SRC /main/groovy
sourceSets {
    main {
        groovy {
            srcDir 'src/main/groovy'}}}Copy the code
  • Create the entry directory and create the code entry directory under SRC /main as follows:

  • Then implement the plug-in codeText.groovyNote that the file suffix isgroovyThe file to be importedpackage com.renxh
package com.renxh

import org.gradle.api.Plugin
import org.gradle.api.Project

class Text implements Plugin<Project> {
    @Override
    void apply(Project project) {
        println("Perform custom plug-ins")
        project.task("haha"){
            doLast{
                println("Implementing a custom plug-in haha Task")}}}}Copy the code
  • The next inmainCreate in directoryresourcesDirectory,resourcesCreate in directoryMETA-INFDirectory,META-INFCreate in directorygradle-pluginsDirectory,gradle-pluginsCreate in directorypropertiesfile
  • propertiesThe file can be named itself, but only after.propertiesEnding, for examplecom.renxh.plugin.properties

  • At the end of the daypropertiesThe file specifies the class in which we implement the plug-inimplementation-class=com.renxh.Text

So far our plugin project has been written. We will introduce our plugin apply plugin:’com.renxh.plugin’ in the module and then execute the plugin Task,./gradlew haha

The output

> Task :mylibrary:Haha Execution custom plug-in HAHA Task Execution phase, task':mylibrary:haha'Time:0ms
Copy the code

This form of writing, in our entire project module can be used, but only limited in this project, other projects can not use

Customize the Module and upload maven

The second way to write plugins can only be used in this project, but not in other projects. Sometimes we need a plugin to be used in multiple projects, and then we need to upload the plugin to Maven

  • So let’s first set up onejava module, the name can be arbitrary, only to retainbuild.gradleandsrc/main, other files are deleted
  • Modify Gradle contents
apply plugin: 'groovy'  / / must
apply plugin: 'maven'  // This plugin must be used in order to publish to Maven


dependencies {
    implementation gradleApi() / / must
    implementation localGroovy() / / must
}
repositories {
    mavenCentral() / / must
}


def group='com.renxh.cusplugin' / / group
def version='2.0.0' / / version
def artifactId='myplugin' // Unique identifier


// Upload the package to your local Maven repository
uploadArchives {
    repositories {
        mavenDeployer {
            pom.groupId = group
            pom.artifactId = artifactId
            pom.version = version
            // Specify the local maven path, in the project root directory
            repository(url: uri('.. /repos'))}}}Copy the code

In contrast to buildSrc, this adds Maven support and uploadArchives, a Task that packages local plugins and uploadArchives to the local Maven repository. /repos represents the repos under the project root, two of them. Go back two times to the root of the project

The steps to generate groovy files and properties files in SRC /main are the same as in the buildSrc schema

Writing a plug-in

class CustomPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task("cusplugin"){
            doLast{
                println("Cusplugin task perform 111")}}}}Copy the code

Then execute. / gradlew CusPlugin: uploadArchivesuploadArchives tasks, and then you can decorate the repos directory to the project root directory, as shown in figure:

Repos directory is the local Maven repository, com/renxh/cusplugin is the group specified in the script, myplugin is the module name specified in the script and is a unique identifier, 1.0.0 is the version of the script

  • After creating a local Maven repository, you need to reference the plugins in the local Maven repository. First, you need to add the following in the root directory of build.gralde:

buildscript {
    repositories {
        google()
        jcenter()
        // Import the local repository
        maven {
            url uri('./repos') // Specify the local maven path, in the project root directory
        }
    }
    dependencies {
        classpath 'com. Android. Tools. Build: gradle: 3.5.3'
        // Import plug-in dependencies from the local repository
        classpath 'com. Renxh. Cusplugin: myplugin: 1.0.0'

        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files}}Copy the code
  • Classpath specifies the path format, as follows:

These three parameters are configured in the build.gradle script when the repository is built

classpath '[groupId]:[artifactId]:[version]' 
Copy the code
  • Once configured, you can use it in your Module
apply plugin: 'com.android.application'
apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"
apply plugin:'com.renxh.cusplugin'
Copy the code
  • Finally, you can perform the tasks in the plug-in./gradlew app:cusplugin
> Task :app:Cusplugin The Cusplugin task is executed111Execution phase, task':app:cusplugin'Time:0ms
Copy the code

Extension Extension for a plug-in

We can also configure parameters for the plug-in while it is running, and the Extension is used to pass parameters back to the plug-in

I’ll add an Extension to the plugin above

First create an entity class to receive the parameters

package com.renxh.cusplugin
class MyExtension {
    String name;
    int age;
}
Copy the code

Then go to the plug-in class and add the extension

class CustomPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
		// Add the extension
        project.extensions.add('myextension',MyExtension)

        project.task("cusplugin"){
            doLast{
                println("Cusplugin task perform 111")

                MyExtension extension = project.myextension
                //3. Output the extension properties of the plug-in
                println ">>>>>> name: ${extension.name} age:${extension.age}"}}}}Copy the code
  • Add the custom entity class to the extension with a name via project.extension.add
  • Then get the extended entity class by name
  • Finally get the attributes in the extension

Upload Maven again

Once the upload is complete, you can use the extended entity class in your Module

apply plugin: 'com.android.application'
apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"
apply plugin:'com.renxh.cusplugin'

myextension{
    name 'renxh'
    age 27
}
Copy the code

Finally, execute the task,./gradlew app: Cusplugin in the plug-in

> Task :app:Cusplugin The Cusplugin task is executed111
>>>>>> name: renxh age:27Execution phase, task':app:cusplugin'Time:2ms
Copy the code

Nested Extension

We often see this nested extension in Android, as follows:

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.aliyun.idrs.app"
        minSdkVersion 24
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}}Copy the code

DefaultConfig is nested in Android, so how do we implement this form?

Start by creating an inner class

class Inner {
    String b

    void b(String b){
        this.b=b
    }
}
Copy the code

Then add the following code to the original extension

class MyExtension {
    String name;
    int age;

    Inner text = new Inner()

    // Create an internal Extension with the method name text
    void text(Action<Inner> action) {
        action.execute(text)
    }

    // Create an internal Extension with the method name text
    void text(Closure c) {
        org.gradle.util.ConfigureUtil.configure(c, text)
    }
}
Copy the code

The key code here is

    Inner text = new Inner()

  // Create an internal Extension with the method name inner
    void text(Action<Inner> action) {
        action.execute(text)
    }

    // Create an internal Extension with the method name inner
    void text(Closure c) {
        org.gradle.util.ConfigureUtil.configure(c, text)
    }
Copy the code

These two methods are used to create internal extensions. In actual use, only one of these methods is required. Note that the method name must be the same as the name of the member variable

Change the code in the plugin

class CustomPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {

        project.extensions.add('myextension',MyExtension)

        project.task("cusplugin"){
            doLast{
                println("Cusplugin task perform 111")

                MyExtension extension = project.myextension
                //3. Output the extension properties of the plug-in
                println ">>>>>> name: ${extension.name} age:${extension.age}inner:${extension.text.b}"}}}}Copy the code

Then you can use it in build.gradle

apply plugin:'com.renxh.cusplugin'

myextension{
    name 'renxh'
    age 27
    text{
        b  "hahah"}}Copy the code

Execute the plug-in, output

> Task :app:Cusplugin The Cusplugin task is executed111
>>>>>> name: renxh age:27 inner:hahah
Copy the code

Nested Extension in Android

We can click in and look at their source code, which will eventually trace back to the BaseExtension class

    private final DefaultConfig defaultConfig;
    private final NamedDomainObjectContainer<BuildType> buildTypes;

    public void defaultConfig(Action<DefaultConfig> action) {
        this.checkWritability();
        action.execute(this.defaultConfig);
    }
    
     public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
        this.checkWritability();
        action.execute(this.buildTypes);
    }

Copy the code

This is the same as the implementation we described above. If you have time to trace Gradle source code, but the parameters in the nested class are not required, as follows:

myextension{
    name 'renxh'
    age 27
// text{
// b "hahah"
/ /}
}
Copy the code

In other words, you don’t have to write text

Configure a variable number of extensions

Start by creating a class

class Student{

    Student(String name){
        this.name = name
    }
    String name

    String age

    boolean isMale
}
Copy the code

Then add it in the plug-in

class CustomPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {

        NamedDomainObjectContainer<Student> studentContainer = project.container(Student)
        project.extensions.add('team', studentContainer)

        project.task("cusplugin") {
            doLast {
                println("Cusplugin task perform 111")

                NamedDomainObjectContainer<Student> team = project.team

                team.findAll { Student student ->
                    println(student.name + "/" + student.age + "/" + student.isMale)
                }

            }
        }
    }
}
Copy the code

The most important of these is project.container(), which creates a container for managing named objects of the specified type. This is the container that creates Student, which can then be called in build.gradle

team{
    xiaoming{
        age = 19
        isMale =true
    }

    xiaohong{
        age = 18
        isMale =false}}Copy the code

Where xiaoming and Xiaohong are name, the team tag can define multiple Student elements

Output the

> Task :app:Cusplugin The Cusplugin task is executed111
xiaohong18 / /false
xiaoming19 / /trueExecution phase, task':app:cusplugin'Time:1ms
Copy the code

reference

3 ways to customize Gradle plugins for Android

Gradle custom plugin