Welcome to follow my official account, scan the qr code below or search the official id: MXSZGG

This article is based on Android Gradle Plugin 3.0.1

preface

In the previous article, I stated that tasks are functions, so plugins in this article are libraries. After all, writing a large number of tasks in a build.gradle file is not maintainable, so make tasks plugin and apply them directly.

Apply plugin: ‘com.android.application’ to app/build.gradle so that appProject can use tasks in this plugin.

The preparatory work

  1. Create a new Android project.
  2. Create a new Java Library Module, which must be named buildSrc.
  3. willsrc/main/javatosrc/main/groovy

Basic implementation

  1. Create a new xxxplugin. groovy and implement the Plugin interface, for example:

    import org.gradle.api.Plugin
    import org.gradle.api.Project
    
    class TestPlugin implements Plugin<Project> {
      @Override
      void apply(Project project) {
        project.task('pluginTest') {
          doLast {
            println 'Hello World'}}}}Copy the code

As you can see, the plugin above simply creates a task called pluginTest inside the apply() method.

Because the Kotlin/Java and groovy compatible, so does not have to create a groovy file, may also be xxxPlugin. Java/xxxPlugin Kotlin.

  1. Now that plugin is so simple, how can it be applied to a real project? inbuild.gradleAdd the following information to the file:

apply plugin: TestPlugin

After that, call the pluginTest Task on the command line to see if it works

./gradlew pluginTest

> Task :app:testPlugin

Hello from the TestPlugin

extension

As the project grew rapidly, it eventually became clear that instead of printing Hello World, pluginTest tasks could be configured according to the developer’s needs.

  1. Groovy file (which can also be written in Java/Kotlin), which is essentially a JavaBean class like this:

    class TestPluginExtension {
      String message = 'Hello World'
    }
    Copy the code
  2. Get closure information in the Plugin class and print:

    class TestPlugin implements Plugin<Project> {
        void apply(Project project) {
            // Add the 'testExtension' extension object
            def extension = project.extensions.create('testExtension', TestPluginExtension)
            project.task('pluginTest') {
                doLast {
                    println extension.message
                }
            }
        }
    }
    Copy the code

    In the fourth row by project. Extensions. The create (String name, Class < T > type, Object… ConstructionArguments) to get the contents of the testExtension closure and convert the contents of the closure into a TestPluginExtension object using reflection.

  3. Add a testExtension closure to build.gradle:

    testExtension {
     message 'Hello Gradle'
    }
    Copy the code
  4. On the command line type the following information:

./gradlew pluginTest

You should see the output —

> Task :app:pluginTest

Hello Gradle

project

Everything I’ve talked about so far is a normal plug-in that can’t be published to a repository. If you want to publish your plug-in for others to apply to your Project, you need to do the following steps to make your plug-in a Project —

  1. Change the contents of build.gradle file:

    apply plugin: 'groovy'
    
    dependencies {
        compile gradleApi()
        compile localGroovy()
    }
    Copy the code

    External Libraries include gradle-API/gradle-Installation-beacon/Groovy Libraries. The gradle version is based on the gradle Wrapper version configured under the project

  2. SRC /main/resources/ meta-inf /gradle-plugins/.properties, For example, SRC/main/resources/meta-inf/gradle – plugins/com. Sample. The test. The properties, Then put the properities to the implementation of the contents of the documents – class = Plugin path, such as the implementation – class = com. Sample. Test. TestPlugin.

  3. Import the plugin in build.gradle with apply Plugin: ‘plugin name’ — apply plugin: ‘com.sample.test’.

  4. On the command line type the following information:

./gradlew pluginTest

You should see the output —

> Task :app:pluginTest

Hello Gradle

Of course, the above is only to tell readers how to project plugin, does not involve how to submit plugin to the warehouse, about jCenter warehouse submission method can refer to hand to hand teach you how to submit project to jCenter, other warehouse submission method readers can search by themselves.

In actual combat

In the Android packaging process, task after task is executed, and each task performs a specific task (for example, several tasks mentioned in the first article). Therefore, in the development of Gradle plug-ins, if changes are made to the packaging process, In fact most of them are hook one task to achieve a goal – such as our mess by hook transformClassesAndResourcesWithProguardForDebug task (Gradle v2.0 + Task) to realize the confusion of the four components and View; Beautiful said ThinRPlugin by hook transformClassesWithDexForDebug (Gradle v2.0 + task) to implement lean R.c lass/R2. Class.

Because Android’s existing tasks are already pretty good, you just need to know the corresponding task and do something before or after it.

There are few simple examples for the purpose of example. I can only pick up the example in the previous article — create a PIC folder in the app directory and add a PNG image named test. Hook APK packaging process adds the image to the ASSETS folder of APK.

Even though it does seem pretty sterile.

This time, in order to meet the actual development requirements, we can make it more difficult to add images to assets only in the Release package, and the Debug package does not add images to assets. In practice, there are many such requirements. For example, the mess mentioned above confuses the APK source code. Is it necessary for the debug package run by the daily developer to execute this task? Obviously not, this task should only be executed when the release package is released at release time.

So how do you know if the task is currently serving release? It is not possible to simply find a task whose name is packageRelease. There are many variations of the project in daily development. For example, type the following code in app/build.gradle:

android {
	...
	flavorDimensions "api"."mode"
	
	productFlavors {
   		demo {
      		dimension "mode"
    	}

    	full {
      		dimension "mode"
    	}

    	minApi23 {
      		dimension "api"
      		minSdkVersion '23'
    	}

    	minApi21 {
      		dimension "api"
      		minSdkVersion '21'}}Copy the code

At this time, there are 12 variants in total: 3 (DEBUG, Release, androidTest) * 2 (Demo, full) * 2 (minApi23, minApi21).

So how to fill all assets of the release variant package with images?

. According to the official document can know through the android developers can applicationVariants. All access to the current all the apk variant, the variant types as ApplicationVariant, The parent BaseVariantOutput class contains the name field, which is actually the name of the current variant, so you just need to check whether the name field contains the release keyword.

The basic process of creating plugin has been described in the previous article, directly write the core plugin, HookAssetsPlugin source code is as follows:

import com.android.build.gradle.api.ApkVariantOutput
import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.tasks.PackageApplication
import org.gradle.api.Plugin
import org.gradle.api.Project

class HookAssetsPlugin implements Plugin<Project> {
  @Override
  void apply(Project project) {
    project.afterEvaluate {
      project.plugins.withId('com.android.application') {
        project.android.applicationVariants.all { ApplicationVariant variant ->
          variant.outputs.each { ApkVariantOutput variantOutput ->
            if (variantOutput.name.equalsIgnoreCase("release")) {
              variantOutput.packageApplication.doFirst { PackageApplication task ->
                project.copy {
                  from "${project.projectDir.absolutePath}/pic/test.png"
                  into "${task.assets.asPath}"
                }
              }
            }
          }
        }
      }
    }
  }
}
Copy the code
  1. As explained in the first article, all tasks in the current project can only be retrieved in the project.afterEvaluate closure.

  2. Use project.plugins.withid (‘com.android.application’) to ensure that the current project is an Android App project and not an Android Library project. To avoid invalid operations, the Package Task is, after all, a task in com.android.application.

  3. Through the project. The android. ApplicationVariants. All for information on all variants.

  4. By observing the Outputs field in BaseVariant, the parent of the ApplicationVariant class, you know that this field represents the output of the current variant (the DomainObjectCollection type). The packageApplication in ApkVariantOutput, a subclass of BaseVariantOutput, is the PackageAndroidArtifact Task from the previous article.

  5. Determine if the current variant is a variant of Release. (by variantOutput. Name. EqualsIgnoreCase (” release “)/variant. The name, equalsIgnoreCase (” release “) are possible.)

  6. The PackageAndroidArtifact Task described in Hook Step 4 copies the image into assets.

In fact, the more convenient way to find tasks in daily development is to use project.tasks.findByName(name)/project.tasks.getByName(name), which I included in the demo. Source code stamp me.

subsequent

In addition to the above mentioned mess and ThinRPlugin (I will analyze the source code of ThinRPlugin in a future article), THERE are some other well-known Gradle plugins that readers can learn from:

  • Tinker -patch- Gradle -plugin for Android
  • butterknife-gradle-plugin
  • Build-time-tracker-plugin (detects the time taken by each Task when building a project using Gradle)
  • Gradle-small-plugin (Lightweight plug-in framework — Small)

Of course, there are some heavyweight plugins mentioned above, but I don’t know much about them. I can only recommend a plugin by the author of mess source code that can quickly generate r2. Java fields and a plugin that can quickly put specified classes into Maindex. Gradle Plugin is very friendly to beginners.

This article actual combat module source link: please stamp me.

The author has created a new wechat group. If readers have any questions or are interested in the author, welcome to join. Since there are 100 people, we need to add the author’s wechat first.