First, why to pile

We all know that JAVA is object-oriented (inheritance, encapsulation, polymorphism), and the meaning of the peg is aspect oriented (AOP), it is imaginable that one-sided object-oriented development has many limitations, and the combination of aspect programming can be said to complement our limitations. For example: in onClick, it is common to do anti-jitter to avoid opening the page multiple times. The usual implementation is to add a time judgment for each onClick implementation’s second click. The business side can determine the time in the inserted bytecode without writing any code through the method of staking.

From the title name

  • Java bytecode: A virtual instruction format executed by the Java Virtual machine. Generate machine instructions through JVM transformations
  • Peg: The insertion of probes (also called “probes”) into a program while maintaining the original logical integrity of the program under test.

Two, what can piling bring



Three, AOP thought

            

The banking system will have a withdrawal process, we can put the process into a box, the other system will also have a balance of query process, we first put the two processes together, have found that the two are the same validation process, we ring them up to say again first, next ever think can reduce the user authentication code is extracted, That’s where AOP comes in. With AOP, you can write code that doesn’t include the authentication user step, that is, you don’t think about the authentication user at all.

  • What is AOP: centralizing these capabilities, such as monitoring the performance of each method, across and embedded in many modules, into a single place to control and manage them
  • What can it bring me: Don’t modify the source code to the program under the condition of the technique of dynamic unified add functionality, to pick up the scattered on the public portion of the program, make it cut class, so the advantage of reusable code, when it comes to the function requirement change, you can just modify the code, otherwise, you want to change, if as long as modified 1, 2) it can also accept, What if there are 1,000.

Iv. Piling entrance of Android packaging process

                     

This is the whole process of app packaging process, which I divided into the following steps:

  • Aapt to package resource files to generate r.java files
  • Process the AIDL and generate the corresponding. Java interface file
  • Compile the Java file to generate the corresponding.class file
  • Convert the. Class file to the. Dex file supported by the Davik VM
  • Package to generate unsigned. Apk files.

Bytecode peg entry: We know that Android programs from Java source code to executable Apk package analysis of the main two links:

  • Javac: Compile the source file into a file in class format
  • Dex: Collects class files into dex files

If we want to modify the bytecode, we only need to scan the class file after JavAC and before DEX, filter and modify it according to certain rules, so that the modified bytecode will be sent to APK in the subsequent DEX packaging process, which is our peg entry.

Piling method 1: Transform API

Each Transform is actually a Gradle Task. The TaskManager in the Android compiler concatenates each Transform. The first Transform receives the result from the Javac compilation. As well as third-party dependencies that have been pulled locally (jar.aar), and resource resources, note that the resource is not the RES resource in the Android project, but the asset directory. These compiled intermediates flow along a chain of transforms, and each Transform node can process the class and pass it on to the next Transform. Our common confusions, such as Desugar logic, are now implemented in a single Transform, and our custom Transform is inserted at the top of the Transform chain.

For Android Gradle Plugin versions 1.5.0 and above, Google officially provides the TransformAPI as an entrance to bytecode staking.

implementation 'com. Android. Tools. Build: gradle: 1.5.0'Copy the code

Extends Transform Rewrite Transform ()

Pile insertion method 2: Hook dx.jar

Need to introduce Instrumentation



Through Java Instrumentation mechanism, in order to obtain the entrance of pile insertion, for apK build process two pile insertion (hook), the red part in the figure:

Instrumentation refers to applications running on the JVM that can be monitored and assisted by agents that are independent of the application. This monitoring and assistance includes, but is not limited to, capturing JVM runtime status, replacing and modifying class definitions, and so on.

  • The ProcessBuilder class is a new class added in J2SE 1.5 in java.lang for creating operating system processes. It provides a way to start and manage processes. The start method creates a process and pegs it so that the dx.jar process can be started to execute the dex task as follows:

    Java dex. Jar com.android.dx.com mand. Main -- dex... .Copy the code

    Add -javaAgent agent.jar to enable the DEX process to use Java Instrumentation for bytecode staking

  • Approach to our target com.android.dx.com mand in dex process. Main. ProcessClasses byte code insertion, so as to realize into the apk class of each project are carried out in accordance with the rules we filtering and bytecode modification.

The build process uses the VirtualMachine. LoadAgent method described earlier (method 2), and the dex process uses the javaAgent Agent. jar method (method 1).

From there, we have an entry point for bytecode staking, and now we use the ASM library API to scan, filter, and bytecode modify each class in the project.





5. Customize Gradle plugin


Create an Android Library Module project



Build. Gradle: Groovy

apply plugin: 'groovy'

    dependencies {
        compile gradleApi()
        compile localGroovy()
    }Copy the code

Groovy extends Plugin and implements the apply method. Note that the class suffix is.groovy instead of.java



4. Create the Resources directory under main



5. Add the corresponding Maven Deployer to local or remote repositories



6. Use published repositories


Six, ASM


ASM is a Java bytecode manipulation framework. It can be used to dynamically generate classes or enhance the functionality of existing classes. ASM can either generate binary class files directly or dynamically change the behavior of classes before they are loaded into the Java virtual machine. Java classes are stored in rigorously formatted.class files that have enough metadata to parse all the elements of the class: class names, methods, attributes, and Java bytecodes (instructions). After reading information from class files, ASM can change class behavior, analyze class information, and even generate new classes based on user requirements.


Why choose ASM for bytecode weaving?

Framework First time Later times
Javassist 257 5.2
BCEL 473 5.5
ASM 62.4 1.1

You can use a plugin [ASM Bytecode Outline] to write Bytecode in ASM more efficiently

ASM (Core API) accesses each part of a class file in sequence according to the class file structure in a visitor pattern. There are several important visitors.

Operation process

  1. You need to create a ClassReader object that reads the contents of the.class file into a byte array
  2. You then need a ClassWriter object to write back the byte array of the bytecode after the operation
  3. An event filter ClassVisitor is required. A new XXXVisitor object is generated when some of the methods of ClassVisitor are called, and when we need to modify the corresponding content, we simply implement our own XXXVisitor and return

ClassReader class

This class reads the.class file into a byte array in ClassReader, and its Accept method takes a ClassVisitor implementation class and calls the methods in ClassVisitor in order

ClassWriter class

ClassWriter is a subclass of ClassVisitor that corresponds to ClassReader, which reads the.class file into a byte array, ClassWriter outputs the bytecode content of a modified class as a byte array.

ClassVisitor abstract class

  • void visit(int version, int access, String name, String signature, String superName, String[] interfaces) This method is the first called when scanning a class and is used primarily for class declarations. Here is a schematic of the parameters in the method: visit(class version, modifier, class name, generic information, inherited parent, implemented interface)
  • AnnotationVisitor visitAnnotation(String desc, Boolean Visible) This method is called when the scanner scans a class annotation declaration. Here’s an illustration of the various parameters in the method: visitAnnotation(annotation type, whether the annotation is visible in the JVM).
  • FieldVisitor visitField(int Access, String Name, String desc, String Signature, Object Value) This method is called when the scanner scans a field in a class. Here’s a schematic of the parameters in the method: visitField(modifier, field name, field type, generic description, default)
  • MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] Exceptions) This method is called when the scanner detects methods of the class. Here is a schematic of the arguments to the method: visitMethod(modifiers, method names, method signatures, generic information, exceptions thrown)
  • Void visitEnd() This method is called when the scanner has finished scanning the class, if you want to append some methods to the class

  • MethodVisitor & AdviceAdapter

    MethodVisitor is an abstract class that goes into the MethodVisitor interface when ASM’s ClassReader reads a Method. AdviceAdapter is a subclass of MethodVisitor that makes it easier to modify the bytecode of a method.

    AdviceAdapter

    Some of the more important methods are as follows:

    1. Void visitCode() : indicates that ASM starts scanning the method
    2. Void onMethodEnter() : enters this method
    3. Void onMethodExit() : will exit from this method
    4. Void onVisitEnd() : indicates that the method scanning is complete



    Bytecode basics

    • A fully qualified name is a “in a fully qualified name. /, for example:
      Class. Android widgets. AdapterView. OnItemClickListener fully qualified name: android/widget/AdapterView$OnItemClickListenerCopy the code
    • 1. Type descriptors, as shown in the following figure:



    In class files, type Boolean is described by “Z” and array is described by “[” (multi-dimensional arrays can be stacked), so what is the most common custom reference type? Android android.view. view class, descriptor is “Landroid/view/ view;”

    2. The organizational structure of method descriptors is:

    (Parameter type descriptor) Return value descriptor copies codeCopy the code

    Void is replaced by “V”, for example:

    Boolean onGroupClick(ExpandableListView parent, View V, int groupPosition, long ID)  (Landroid/widget/ExpandableListView; Landroid/view/View; IJ)ZCopy the code




    A detailed explanation of the three steps in the figure above:

    Step one:

    The ASM ClassVisitor scans the class files of all classes. VisitMethod () determines if it is BaseActivity. If it is BaseActivity, abort the scan if it is not.

    Step 2:

    Every time the ClassVisitor scans a method, visitMethod makes the following decision:

    1. Is the

      method to filter

    If it passes, the scanned method is proven to be one that requires bytecode injection, and the scan logic is handed over to the MethodVisitor for bytecode modification (step 3).

    Step 3: Modify the method bytecode scanned

    Assume that the method to be modified is as follows:

    public int test() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }}Copy the code

    After modification, it needs to be changed to:

    public int test() { long startTime = System.currentTimeMillis(); try { Thread.sleep(1000); } catch (InterruptedException e){ e.printStackTrace(); } long timing = System.currentTimeMillis() - startTime; BlockManager.timingPage(getLocalClassName(), timing); }Copy the code