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