I mentioned plugins in the previous section, “Making Up the Android Skill Tree — Playing with Gradle(2)”.

Gradle itself does not provide compilation packaging. It is a framework that defines processes and rules. All compilation is done by plug-ins, such as Java with Java plug-in and Kotlin with Kotlin plug-in. The essence of a plug-in is to define tasks and implement templates for those tasks.

In this section, we can learn about Gradle plug-in compilation and release related postings. Readers can also refer to the official document: Packaging a Plugin

Gradle plugin is essentially a JAR file that can be used to create projects with idea or with the Gradle init command, as shown in the following example:

You can use Groovy, Java, and Kotlin to implement your script. You can use Groovy, Kotlin, and Java to implement your script

0x1 Gradle plugin classification

1. Build scripts

Not strictly a plug-in, you write your build script code into a separate file and copy and paste it into your project directory for reference. The following is an example:

External build script: other.gradle

// Internal access to scripts
def versionName = "v1.0.0"
def versionDesc = "First edition"

// Script external access
ext {
    author = "CoderPig"
}

/ / Task
task printVersionInfo {
    doLast {
        println "$versionName → $versionDesc"}}Copy the code

This script is referenced in build.gradle:

apply from: 'other.gradle'

task test {
    dependsOn(printVersionInfo)
    doFirst { println(author) }
    doLast { println(In the "build. Gradle task")}}Copy the code

Type gradle test to execute the task.


2. BuildSrc project

When Gradle is executed, the buildSrc directory in the root directory is compiled as the plug-in source directory and the results are added to the build script’s classpath.

This plugin does not require plugins{}, and is generally suitable for the development and debugging of plug-ins that do not have reuse. There is also a fault: you cannot use the property configuration DSL, and need to configure<… > {… } Configure plug-in properties.


3. Independent projects

Jar a project into a jar package that can be reused across multiple projects


0x2 Gradle Plugin Demo First Experience

We have created a Gradle plugin project from the command line, and have a look at the core elements of the Gradle plugin.

1. Project Composition

Groovy: cppluginplugin.groovy

package cn.coderpig.plugins

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

public class CpPluginPlugin implements Plugin<Project> {
    public void apply(Project project) {
        // Register a Task
        project.tasks.register("greeting") {
            doLast {
                println("Hello from plugin 'cn.coderpig.plugins.greeting'")}}}}Copy the code

The code is fairly straightforward, implementing the Plugin interface, specifying the generic class as Project, defining the Apply method, and registering a Task named Greeting with closure that prints a sentence.

② Build. Gradle in the root directory

// Reference Java Gradle plugin development plugin, Groovy support
plugins {
    id 'java-gradle-plugin'
    id 'groovy'
}

gradlePlugin {
    plugins {
        greeting {
            / / the plugin id
            id = 'cn.coderpig.plugins.greeting'
            // Plug-in implementation class
            implementationClass = 'cn.coderpig.plugins.CpPluginPlugin'}}}Copy the code

Many Gradle plugin development tutorials require you to configure a properties file, such as:

src/main/resources/META-INF/gradle-plugins/cn.coderpig.plugins.greeting.properties

The content of the document is as follows:

implementation-class=cn.coderpig.plugins.CpPluginPlugin

In fact, gradlePlugin can be declared in build.gradle without having to configure it again!


2. Publish plug-ins locally

Plugins can be imported using the buildSrc method above, or they can be jar first, distributed locally for use, and distributed to Maven or JCenter repositories for others to use. To try publishing locally, you need to add maven-related configurations:

plugins {
    // Add the Maven plugin
    id 'maven'
}

uploadArchives {
    repositories.mavenDeployer {
        repository(url: uri('C:\ Users\ user name \\Maven\ repo'))   // The local repository path
        pom.groupId = "cn.coderpig.plugins"// Unique identifier (usually the module package name, but can be arbitrary)
        pom.artifactId = "CpPluginPlugin" // Project name (usually the name of the class library module, but optionally)
        pom.version = "0.0.1"      / / version number}}Copy the code

UploadArchives is a Task that publishes the plugin class library to the repository:

Double-click to execute the task and generate the following files under C:\Users\ username \Maven\repo:

After publishing to local Maven, you can reference the validation plugin effect in another project by modifying the build.gradle root directory:

buildscript {
    repositories {
        ...
        // Local Maven address
        maven { url 'C:\ Users\ user name \\Maven\ repo' }
    }
    dependencies {
        ...
        // Plugin dependencies
        classpath "Cn. Coderpig. Plugins: cpplugin: 0.0.1." "}}Copy the code

Build. gradle in the app directory or module directory references this plugin:

plugins {
    ...
    id 'cn.coderpig.plugins.greeting'
}
Copy the code

Then write a Task to verify:

task("testPlugin") {
    group("custom") // Group tasks to find tasks
    dependsOn('greeting')   // Call the greeting Task in the plugin and execute testPlugin
    doLast { println 'Mission completed'}}Copy the code

The running results are as follows:


3. Plug-in remote publishing

① Abandoned JCenter

When it comes to remote distribution of plugins, nine out of ten tutorials are uploaded to JFlog Bintray.

In fact, as early as March 3 of this year, an official announcement was made:

A number of package management and distribution services, including GoCenter, Bintray and JCenter, will cease operations.

Since 3.31 no new submissions will be accepted, you will still be able to pull libraries submitted before 2021.3.31 until 2022.2.1.

If you can’t commit, you’ll have to find an alternative to JCenter, which is a Gradle plugin. You can try to submit to Gradle’s remote repository.


② Gradle Plugin repository

For a complete process introduction, see the official document: How do I Add my Plugin to the Plugin Portal? , here are the steps:

First register an account (Github authorized login is also available), and then click to generate API Keys:

Copy and paste to the local gradle configuration file: HOME_DIR /. Gradle/gradle. The properties (~ /. Gradle/gradle. Properties)

How do I use the Plugin Publishing Plugin? Com.gradle. plugin-publish specifies the parameters required by com.gradle.plugin-publish.

plugins {
    ...
    id "com.gradle.plugin-publish" version "0.14.0" // Upload the plugin
}

version = "0.0.1"   // Customize the plug-in version
group = "cn.coderpig.plugins"   // Customize plug-in grouping

// Customize the plug-in ID and implementation class
gradlePlugin {
    plugins {
        greeting {
            id = 'cn.coderpig.plugins.greeting'
            implementationClass = 'cn.coderpig.plugins.CpPluginPlugin'}}}// Additional information about the plug-in
pluginBundle {
    website = 'https://github.com/coderpig/cpplugin'
    vcsUrl = 'https://github.com/coderpig/cpplugin'
    description = 'cpplugin gradle plugin' // Plug-in description
    tags = ['cp'] // Search for keywords
    plugins {
        greeting {
            // The id is automatically obtained from the java-gradle-plugin
            displayName = 'cpplugin gradle plugin'}}}Copy the code

After Sync Now is configured and the next project is rebuilt, a plugin portal directory will appear in the Gradle window. Click on publishPlugins to publish the plugin to Gradle:

After the successful release of the need to wait for the official review, review through the word can be in the official search to their plug-in, of course, MY scribble and information disorderly Demo is certainly not review, 2333, just to demonstrate the process ~


(3) JitPack warehouse

The Gradle Plugin repository is only suitable for publishing Gradle plugins. It is not supported by third-party libraries for daily use.

The Github repository is based on the Github repository, and the publishing method is not complicated. Follow the official user guide: Use the Maven Publish plugin

Add the configuration information to your library’s build.gradle file, then push it to Github, click the Releases panel → Create New Release, enter the release number, title and description, and then click Publish Release.

Then go back to the JitPack, locate the library I wrote, and then click On Releases → Get It and wait for a while:

The red color here indicates that the library failed to compile because it did not add the configuration. The normal compilation is green, and below you can see how to rely on the library in the project, replacing the Tag with the corresponding version, such as 1.0.2 here:


(4) mavenCentral warehouse

Registration is required before uploading libraries to Mavan Central login: Sonatype, after entering the page, click Sign Up to enter the registration page to register:

After you have a Sonatype account, open the admin background and Log In. The following error box will pop up:

To apply for a wave of Sonatype upload permissions, go back to the issues.sonatype.org/ page, click New, and fill in the project information:

After submitting the domain name for approval, you will be asked to prove that the domain name is your own:

The first solution is the simplest, domain name DNS sit down resolution, add a TXT type record can be, such as:

Gradle configuration, GPG signature, etc. For more details, please refer to: The process of publishing Android libraries to MavenCentral

0x3, common API leakage

To create a configuration DSL, first define the DSL structure, define the properties within it, and then add the following to the Plugin Apply method:

TestExtension extension = project.getExtensions().create("testExt", TestExtension)
project.extensions.add("testExt", TestExtension)

project.task("TestTask") {
    doLast {
        //2. Get TestExtension for external configuration
        TestExtension extension = project.testExt
        //3. Output plug-in extension properties
        println "> > > > > >" + extension.message
    }
}

testExt {
    // Assign a value to the plugin extension's properties
    message  "helloworld"
}
Copy the code

0x4 plug-in source exploration — Meituan channel package generates plug-in Walle

No idea, forced to write a useless plug-in no meaning, just the group of people talking about packaging plug-in: Walle, directly see how others plug-in is implemented ~

Compared with Android packaged to a lot faster, in the United States technical team blog has introduced the tool about the implementation principle: a new generation of open source Android channel package generation tool Walle, a brief talk, and so on slowly with a wave of source ~

First, APK package difference before and after v2 signature:

The APK Signing Block is used to ensure that the other three blocks are protected, so any changes made to these blocks cannot escape the application Signing scheme. The APK Signing Block is the starting point for meituan’s package plugin. The Block 2 format is described as follows:

V2 signature information is stored in this block with ID (0x7109871A) id-value. Android ignores other id-values. The package plug-in defines a custom id-value to write channel information to this area, and the App runs to read the channel information, and then completes the specific channel initialization. The plug-in is mainly composed of four parts:

  • ① Java class library for writing id-value information;
  • (2) Gradle builds plug-ins to integrate with the Android packaging process;
  • ③ Java class library for reading id-value information;
  • ④ AAR for reading channel information for com.Android. application;

Ok, the general principle of understanding here, followed by a wave of plugin specific implementation source, directly to the plug-in configuration file:

Open the interface implementation class to see which is:

1. GradlePlugin

Go to gradleplugin.groovy and the logic is not complicated:

Navigate to extension.groovy, which is the argument passed by the DSL:

Here is the corresponding document:

After applyExtension(), look at applyTask() :

Follow the ChannelMaker Task class

2. ChannelMaker

Integrate DefaultTask, @taskAction annotation to identify the Task’s own method to execute:

And then determine

The method corresponding to the generated channel APK is invoked in the following cases:

  • PROPERTY_CHANNEL_LIST – channelList. Each {generateChannelApk (…). }
  • PROPERTY_CONFIG_FILE – generateChannelApkByConfigFile (…).
  • PROPERTY_CHANNEL_FILE – generateChannelApkByChannelFile (…).
  • ConfigFile instanceof File – > generateChannelApkByConfigFile (…).
  • ChannelFile instanceof File – > generateChannelApkByChannelFile (…).
  • VariantConfigFileName! = null && variantConfigFileName. Length () > 0 – generateChannelApkByConfigFile (…).

GenerateChannelApk: generateChannelApk: generateChannelApk: generateChannelApk: generateChannelApk

How does ChannelWriter write channel information

3. ChannelWriter

Followed by the put() method, we end up calling:

Follow the putRaw() method:

APK_CHANNEL_BLOCK_ID:

This plugin writes channel information to the ID in the APK Signing Block, followed by payloadwriter.put ().

4. PayloadWriter

Step by step, follow up to putAll, see how complicated the code looks? The ApkSigningBlockHandler callback interface defines a handle method:

It passes in an originIdValues (the id-value of the APK itself), iterates through the new idValues, writes to them, and returns the data in the ApkSigningBlock instance with addPayload().

Then some processing of V3 signature, and then write channel information to APK, here is really technical, too strong!!

Byte level fine file operation, this foundation… ApkSigningBlock → writeApkSigningBlock()

Write the contents of block 2 back to APK, corresponding to the table above:

That’s the general process for getting channel pack information into APK, and the timing of the task is assemble.

The official document reads:

The APP also relies on this AAR to read the corresponding channel information at runtime

Follow WalleChannelReader in the project’s Library directory:

5. WalleChannelReader

Pass comtext to get the path to apk, then pass channelreader.get () :

6. ChannelReader

With the getMap () :

With the PayloadReader. Get string ()

7. PayloadReader

Get () → getAll()

Channel object RandomAccessFile. GetChannel () file, and then the incoming ApkUtil. FindApkSigningBlock ()

Apkutil. findIdValues(apkSigningBlock2) ChannelReader → get()

This is the implementation of calling getChannel(), or getChannelInfoMap() if you get by key.

The above is the complete explanation of this plug-in implementation, of course, the core difficult Byte level file operation, after solving the APK construction process to study ~


References:

  • Mastering Gradle
  • Are you sure you want to create a dependency library on the JitPack?

This article is part of the “Gold Nuggets For Free!” Event, click to view details of the event