What is the Gradle Plugin

Gradle is a framework that defines processes and rules through plug-ins. For example, compile Java plug-ins, compile Groovy plug-ins, compile Android APP plug-ins. Gradle plug-ins simply encapsulate the reusable parts of your build logic and apply them to different projects and builds. Gradle plug-ins intervene in the build process to extend functionality.

What is a Transform

Gradle Transform is a standard Android API that developers can use to modify.class files during the class->dex phase of a project. At present, bytecode staking technology is more commonly used

The Android packaging process

Take the application’s class file at the red arrow using the Transform API, walk through the methods in the class file, find the method we need to change, modify the target method, insert our code and save, that’s bytecode pegging.

A Transform is not required, as long as you find the node before class compiles the task to dex. Inserting a custom task by before is also possible, but using a Transform is easier.

Customize Gradle Plugin

3.1 There are three ways to customize Gradle plug-ins:

1) Build scripts

You can include the source code for your plug-in directly in your build script. Disadvantages: The plug-in is only visible within the build script in which it is defined and cannot be reused in other scripts.

2) buildSrc

Gradle automatically finds and compiles plug-ins in the buildSrc module and makes them available in the classpath of the build script. The plug-in is visible to every build script throughout the project, but it is not visible outside the project, so it cannot be reused in other projects. Advantages: Convenient debugging.

3) Independent projects

You can create a single project for your plug-in, package the project into a JAR package, and then reuse it across multiple projects

2) and 3) you can upload the plugin to maven library or locally by creating a uploadArchives Task.

uploadArchives{ repositories.mavenDeployer { repository(url: uri(".. /repos")) // repos is the local address, GroupId = "com.example.plugin" pom.artifactid = "AsmPlugin" pom.version = "1.0.2"}}Copy the code

3.2 Steps for customizing Gradle plug-ins

(1) Create a new Android Library project or use buildSrc

(2) Create a groovy or Java directory and a Resources directory under the main directory. Groovy or Java directory is used to write plugin logic. Resources directory is used to declare custom plugins.

(3) The method of writing plug-in is to write a class to implement Plugin class, and implement its apply method, and complete the plug-in logic in the apply method; A custom Tranform can be registered in a custom Plugin.

class AsmPlugin implements Plugin<Project> { @Override public void apply(Project project) { System.out.println("AsmPlugin executes ") project.getextensions ().findBytype (appExtension.class).registerTransform(new) DemoTransform(project)) } }Copy the code

After registration, in the build process will pass TaskManager# createPostCompilationTasks for this custom Transform generates a corresponding Task, (name rules as follows: TransformClasses +With+ custom transform name +For+Debug/Release) executes this Task in the process of converting a.class file to a.dex file. Transform all.class files (including.classes from third-party libraries), using the Transform logic defined in the Transform method of Transform

Create a (plugin.) properties file in the Resources directory (/ meta-INF /gradle-plugins) and declare your own plugins. The name of the properties file is the name we used to apply the plug-in

(5) Apply the plugin to the Gradle file of app

Apply plugin: ‘com.example.xx-plugin’// Resources/meta-INF /gradle-plugins properties file name

Customize Transform

public class DemoTransform extends Transform { Project project; public DemoTransform(Project project) { this.project = project; } @Override public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { super.transform(transformInvocation); // Consumer input, from which you can get the JAR package and the class folder path. Need to output to the next task Collection < TransformInput > inputs. = transformInvocation getInputs (); // Reference input, no output required. Collection<TransformInput> referencedInputs = transformInvocation.getReferencedInputs(); / / OutputProvider management output path TransformOutputProvider OutputProvider = transformInvocation. GetOutputProvider (); For (TransformInput input: inputs) {for(JarInput: inputs) input.getJarInputs()) { System.out.println("jar= " + jarInput.getName()); File dest = outputProvider.getContentLocation( jarInput.getFile().getAbsolutePath(), jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR); Fileutils.copyfile (jarinput.getFile (), dest); } class for(DirectoryInput DirectoryInput: input.getDirectoryInputs()) { if(directoryInput.getFile().isDirectory()){ for (File file : FileUtils.getAllFiles(directoryInput.getFile())) { System.out.println("directoryInput--"+file.getName()); } } File dest = outputProvider.getContentLocation( directoryInput.getName(), directoryInput.getContentTypes(), directoryInput.getScopes(), Format.DIRECTORY); // create folder fileutils.mkdirs (dest); Fileutils.copydirectory (directoryInput. GetFile (), dest); fileutils.copyDirectory (directoryInput. GetFile (), dest); }}} @override public String getName() {return "DemoTransform"; } / / Transform to deal with the type of @ Override public Set < QualifiedContent. ContentType > getInputTypes () {return TransformManager.CONTENT_CLASS; } // transformmanager.scope_full_project@override public Set<? super QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT; } @override public Boolean isIncremental() {return true; }Copy the code

In the transform method, we need to copy each JAR package and class file to the dest path, which is the input data for the next transform. When copying, you can make some changes to the bytecode of the JAR package and the class file before copying

4.1 Incremental compilation is supported

1) Overwrite the Transform interface method: isIncremental(), return true.

2) Check whether the current compilation is incremental for Transform:

If it is not incremental compilation, all class files are processed as before. (For example, if the first compile after clean does not have incremental basis, even if isIncremental for Transform is put back to true, the current compile is not incremental for Transform, so all class files need to be processed in sequence.) Based on the Status of each file, the file is processed: (NOTCHANGED: the current file does not need to be processed, or even copied; ADDED, CHANGED: Normally processed, output to the next task. REMOVED: Removes files from the outputProvider path.)

Note that the current compilation is affected by two things: (1) the return value of the isIncremental() method; (2) whether the current compilation has an incremental basis; (The first compilation after Clean has no incremental basis, and subsequent compilations have incremental basis)

The incremental time is shortened to a full speed increase of more than 3 times, and this speed optimization becomes more significant as the project gets larger

5. What is ASM

ASM is a Java bytecode level code analysis and modification tool. ASM aims to generate, transform, and analyze compiled Java class files. You can use ASM tools to read, write, and transform JVM instruction sets. In plain English, it is used to process javac compiled class files

The working principle of ASM is shown as follows:

• ClassReader: Used to read already compiled. Class files.

• ClassWriter: used to rebuild compiled classes, such as changing class names, attributes, and methods, and to generate bytecode files for new classes.

• A variety of Visitor classes: As mentioned above, the CoreAPI processes from top to bottom based on the bytecode, with different visitors for different areas of the bytecode file, Such as MethodVisitor to access methods, FieldVisitor to access class variables, AnnotationVisitor to access annotations, and so on. To implement AOP, the focus is on using the MethodVisitor

How to use ASM

ASM can be developed using the ASM Bytecode Outline plug-in because it is based on the JVM instruction set.

Using this plug-in helps you look at bytecode and generate ASM code directly. We can first add the code to the corresponding class and then run the code->Asm Bytecode Viewer to get the corresponding ASMified code.