Android Hot fix Framework robust principle + and the framework code from "closed source" into "open source" (the next chapter)

Source: Internet
Author: User

I. Review of the framework principle

This article continues to look at the Heat repair framework robust principle, in a previous article has explained in detail: Robust framework principle, because this framework is not open source, so through the official introduction of the principle, we have simulated the case and the framework of the simple practice of logic. Finally in the anti-compilation of the American group app to verify that the logic of the implementation of the general is not bad. The logic of the final determination of practice is similar. But at the end of the last article it was emphasized that this framework attracts me not to study his hot fix technology, but that he has a technical point, that is, how to add a repair function code to each of the classes at compile time, which is transparent to the upper development code . As can be seen from the previous case, if the method does not repair the function code, then this method has lost the repair function, and then look at the schematic diagram of this framework, including the compile-time dynamic insert code and load the repair package logic:



second, the principle of automatic insertion analysis

So here's a detailed introduction to the compilation period how this framework inserts each method in a project into a fixed code. In introducing this knowledge point, you can first understand how the ASM package is used in Java to manipulate bytecode logic. Or you can take a look at this article: The Dynamic Insert Code tool in Android Icodetools This article has detailed how to insert a piece of code in each method of each class. In fact, this article is based on this technology to operate. But the code inserted here is more complicated than that. Not much to explain, directly to see how to operate.

For demonstration and pits convenience, we had better start using a simple case, because for the first time no one can guarantee the success of the successful insertion. So here's a simple class file to go with. This defines a simple class person that internally defines several different types of methods, including the return value of the method, the parameter, the type, and so on. This is also to see if the various scenarios in which we insert the code will succeed. We have only one purpose, that is, how to dynamically inject each method in this class into the dynamic code mentioned earlier:

if (changequickredirect! = null) {if (Patchproxy.issupport (New Object[]{xxx,xxx,...}, this, Changequickredirect, false) {return ((XXX) Patchproxy.accessdispatch (new object[0], this, Changequickredirect, false));}}

Insert a static variable in the class:

public static Changequickredirect Changequickredirect;

The person class that we define is as follows:


This class is very simple and defines a number of different types of formats, and we'll write code that automatically injects the fix code for each method and adds a static variable to the class. With the previous article: Android in the dynamic Insert Code tool Icodetools, we operate is very simple, here still need to use the ASM package and Eclipse plug-in bytecode, We use the bytecode plugin to see the ASM code for that code, but it is important to note that each method inserts a different code, first of all, to look at the two methods of fixing the code: Issupport and Accessdispatch, both of which have four parameters:

The first parameter: an object array that holds all the parameter values for this method, and sees if the basic type needs to be converted into a box.

The second argument: the object to which the current method belongs, if the method is static type is null if the method is non-static is this.

The third parameter: Fix the interface type, which is the static variable changequickredirect we need to insert.

Fourth parameter: Whether the method is a static type.

So from the above four parameters, we know that we need to do the following when inserting code, mainly include the following points:

1. The parameters of each method are different , because we see that the first parameter of the Issupport and Accessdispatch methods of the inserted fix code is an object array, which is all the parameters of this method.

2, method type declaration is different , if one method is static type, the second parameter of Issupport and Accessdispatch method is null, and the last parameter is true, otherwise this and false value.

3, the return value of the method is different , for the method has a return value need to do special processing, and the method return value type is also to do processing.

Mainly these three points, but the actual operation has a lot of small details, such as parameters if it is the basic type, we have to do a box operation, it becomes the object type. If the return value is a basic type, you also have to do a unboxing operation to change the object type to the base type .


Three, automatic insert case

The above analysis of the basic principle, the following directly to operate, we began to use a simple method to do the case, and then manually insert a repair code, in the use of the bytecode plugin to see the code corresponding to the ASM code:


With ASM code, we need to be aware of the parameter array build, and the return value conversions:


Here we can copy the ASM code directly into the Java code, in this process, we need to deal with that parameter array construction, because now the method parameter number is indeterminate, so we have to write dynamic building code:


This code is to complete the dynamic insertion of the repair code, logic and order is clear, first of all to construct a method of four parameters, the most important of which is the first parameter of the object array.


First parameter: Building method parameter Array


In this case, it is also necessary to distinguish whether a method is parameter-and-parameter-free. Do special processing, and then the most central place is to create multiple parameter array types of code:


The above code begins by creating an array of all the parameter types for a method, which requires the following special handling:

1, because the byte code instruction in the constant value instruction is opcode.iconst_0 to opcode.iconst_5, so if an array size exceeds the scope of this instruction, you have to use Opcode.bipush to operate.

2, determine whether the current method is static type, because this type is related to the index value of the local parameters of the method, we know that the method of non-static type has an implied parameter this, so here to do a local parameter index value judgment. The static type starts at 0, and the non-static type starts at 1.

3, when the array data is populated, because the need to access through the index value, here still have to do special processing, more than 5 through the Opcode.bipush instructions to operate.

4, for parameter processing needs to distinguish between the basic type and object type, because they use different load instructions, the general basic type of long is lload,double is Dload,float is Fload, the other basic types are iload, for the object type is aload.

5, for parameters, if a parameter of the previous parameter is the long,double type, to do special processing of the parameter index, here the conjecture may be related to the number of bytes occupied by the two types, after all, they are occupied 8 bytes. The other types are within 4 bytes. When the two types are encountered, the parameter index value has to be added one.

See there are so many pits, can think of me in pits how painful, but the pits method is very simple, you can first simulate the definition of such a method, and then look at his corresponding ASM code:


This method contains multiple parameters, and all special cases are included to see the ASM code:


So we have finished filling the pit. Continue to look at the above code, in dealing with special basic types, because the above mentioned basic types in addition to the load directive is not the same, there is the need to do object marshaling, also can be seen from the ASM code, see the specific method:


For special handling of different basic types, here's a look at the Boolean type of processing:


The other basic types are roughly the same and are no longer explained here.


Second parameter: The Class object to which the current method belongs

We're done here. The first parameter of the Fix method: Object array Building, is also the core of the whole process, but also the most complex. We go back and look, the second argument: the object that the method currently belongs to


The decision here is whether the method is a static type, and if the static type is passed directly to NULL, if the non-static type is to pass directly to the implied first parameter, this.


Third parameter: Static variable Changequickredirect

This parameter is simple, changequickredirect the static variable of the class directly:



Fourth parameter: Whether the method is static type



With the above four parameters, the following can begin to call the repair of the two methods, one is Issupport:


The return value of this method is the Boolean type, which is executed in the IF statement, which can be used with the IFEQ directive. But there is a hole here, that is, if the bytecode plug-in directly get the ASM code, the method parameter signature is the first ljava/lang/object;, this is obviously wrong, because we know that the first parameter is an array type, so we need to manually change to [Ljava/lang /object, the pit looked for a long time to fill the success.


Then there is the Accessdispatch method call, and we still need to construct four parameters before calling this method, but this construction process is identical to the previous one. Directly copied over it, mainly after the implementation of this method, and there are many pits:


As we can see here, we have to pits the same as the complex method parameter array constructed above. Here are some special treatments that need to be done:

1, whether the method has a return value, if there is no return value, call the Opcode.return directive directly.

2. The method return value type requires special handling if it is a basic type.

3, the method return value type is the object type, need to do type signature processing, if the array type does not handle, if the non-array type needs to remove the preceding L character, as well as the following semicolon character, or later when using the DX command to convert the jar error.

Let's see if the return value is a basic type, we need to do a unboxing, that is, to change the object type to the basic type:


The code is simple, just copy the ASM code and make a judgment on each of the basic types. The last is the return instruction, because the different base types and object types differ, the type of float in the base type is the Freturn,long type is the lreturn,double type is Dreturn, the other types are Ireturn, If the object type is directly Areturn :



Iv. Problems encountered

Here we have completed the writing of dynamic code injection, the whole process can be seen there are many places to deal with, that is, pits, in countless experiments encountered problems to solve problems, because if you start to copy ASM corresponding code will encounter some problems. But the solution is simple every time you encounter a problem, using the Jd-gui tool to see the class files that we've processed each time, like this:


Here to see, this method processing on the error, in fact, this is the hole that was encountered before, if a parameter before a parameter is the long,double type did not do special processing results. At this point, we can manually write the fix code and then use Eclipse's bytecode plugin to look at its corresponding ASM code, and compare it to our generated code logic.

There is also a way to use the JAVAP command to generate two class bytecode, and then contrast can also:


Then compare the bytecode of these two class files:


Different places, and then continue to modify the instructions.


v. The pits that have been trampled

Here we write the logic of the dynamic Insert code, summarizing the pits we encountered:

First, handle the constructor method parameter array

1, the number of parameters, byte code instruction constant value is iconst_0 to iconst_5, over this range, you have to use Bipush instructions.

2, the basic type needs to be sealed box operation.

3, the parameter is preceded by a long and double type, need to do special processing.

4. The basic type and object type use different load instructions when storing values.

Second, method return value processing

1, the method has no value return.

2, the return value is the basic type need to do unpacking processing.

3, for the return value is an array and non-array type processing.

4. The basic type and object type return directives are different.


six, packaging into small tools

The following is not finished, because we see that just finished writing the code to insert the tool class method, we can see that this method needs to pass through several parameters:


The meanings of these parameters are explained below:

First parameter: The class of the action method Methodvisitor

Second parameter: The full name of the class to which the method belongs

Third parameter: List of method parameter signature strings

Fourth parameter: Method return value type signature

Fifth parameter: Whether the method is static type

Here we need to use the API in the ASM package to process class files, before the introduction of the Dynamic Insert Code tool Icodetools in Android, said that the operation class using Classvisitor, Operation method using Methodvisitor can , directly see the code:


Here you can get the method's parameter type and return value type through the type class by describing the method's description field desc.


Here, you can get the method is static type through the Access field, and you need to add a static variable to each class Changequickredirect


Then you need to use the Classreader class, where you pass in a byte array of classes that need to be processed, and then get to the class name. After processing, return the byte array of the class.

External in the encapsulation of a method, read and write files, here in order to facilitate the use of the back, wrote two simple gadgets, one for a separate class file processing, one for the jar file processing, as long as the input source file, the output is the result of processing:


The specific code in this project is not much explained, the following will give the project, you can get down and slowly read. But it's important to note here that the Changequickredirect and patchproxy two classes must be consistent with the name Package name in the application project, or the insertion will fail . Here's a simple process of handling the person class with a single class file tool:


All right, here we go. The logic of the robust framework dynamically inserting code. Two tools are available, one dealing with jar files and one dealing with individual class files. So some classmates may be confused? There should be other things to do in the US program. That's true.


Vii. How to use tools in the project

With these two tools, we can export it to a JAR file, start the operation during the project compile, regardless of the project with the ant script, or the Gradle script, do not know the script to compile Android app classmate, you can view here: Android using script to compile the app : Compiling a project with a script has to go through a few stages.

1. Generate R.java class files using the Aapt.exe provided by the Android SDK
2. Use the Aidl.exe provided by the Android SDK to turn the. Aidl into a. java file (Skip this step if there is no aidl)
3. Compile the. Java class file by using the Javac.exe provided by the JDK to generate the class file
4. Generate Classes.dex files using the Dx.bat command line script provided by the Android SDK
5. Generate resource bundle files (including res, assets, androidmanifest.xml, etc.) using the Aapt.exe provided by the Android SDK
6. Using the Apkbuilder.bat provided by the Android SDK to generate an unsigned apk installation file
7. Use the JDK's jarsigner.exe to sign an unsigned package with an APK signature


Then the script is in our own control, so you can choose between two stages, there are two scenarios:

The first scenario: after compiling the Java file with the Javac command into a class file, use the one above to process a single class file tool. This is no sense for developers. Completed automatically during the compile phase.

The second scenario: after compiling all the files to get the class file, package it into a jar file and then process it using the processing Jar file tool mentioned above. Then use the DX command to turn the processed jar file into a dex file.

In fact, no matter what kind of solution, as long as the compilation period to find the right time, using the two tools given above can be done. In fact, there is a way of thinking, it is necessary to use the Icodetools tool mentioned earlier, need to change the tool, the dynamic insertion code in this article logic to the Icodetools tool, and then we can enter an APK, the output of the APK is already added successful results. But this is not advisable, I believe the group will not use this way of thinking to deal with.


Eight, optimize the work

Here we are going to explain the logic of dynamically inserting the fix code into the robust framework, but here are some details that need to be addressed:

1, add the blacklist rule, we can see that this dynamic insert code snippet is to repair the role, then an apk in all the classes are necessary to insert it? Obviously not required, for example, we use the class in the V4 package, then the class here definitely does not need to be inserted. Of course there are some methods of our own defined classes that do not want to be inserted. So here is going to have a blacklist of inserts, this need in the above Insert tool to do processing, relatively simple, because we know the method name and the class name of the processing, just do a simple filter can.

2, see from above each class need to have a changequickredirect variable, this variable name is unique, but can not guarantee in the development process, each developer will use this name, if someone used, and we automatically inserted, then compile will definitely error. So we need to make some judgment logic before inserting the code. If this variable is not inserted. and give some information hints.


ix. Advantages and disadvantages of the framework

Combining the previous framework principles and the knowledge of this article, here's a look at the pros and cons of this hot fix framework:

Pros: In a previous article, it was known that his loading logic was very simple, and the fix pack could be loaded directly using the Dexclassloader class. So you can see the compatibility of this repair framework is very good. Because direct use of the system-provided API, there is no high crash rate, unlike the Andfix framework with the help of the bottom, there will be system restrictions need to do compatible operations.

Cons: From this article can be learned that an enterprise application code itself is very large, in this way to each class each method is inserted into this code, it is conceivable that the insertion code after the APK package is how large. There are also some confusion issues, like the Andfix framework, which does not support resource remediation.

So if the framework is really integrated into the project there are a lot of pits we need to fill out, of course, this is not the scope of this article. Interested students can go online search on the robust framework of the problem, there are detailed instructions. Heat Repair Road slowly its repair far XI, I will be up and down and pits!


Project: Https://github.com/fourbrother/RobustInsertCodeTools


10. Summary

This article mainly continues the previous article introduced the robust framework principle and the practice case, looked at this framework's core technical point is how to automatically give each class each method in the compile time code, with the ASM package and the bytecode plug-in completes. And this meaning is not confined to the study of the robust framework, but for the subsequent operation is useful, that is, if there is automatic insertion of code logic later, this article is also a very good case. We will continue to analyze the last hot fix frame tinker on the market. The last small series of the weekend to write really good tired, remember after watching a lot of diffusion sharing, if there is a better reward.


Click here for more information:

Focus on the public, the latest technology dry real-time push

Sweep and make a small series
Add the Note: "Code beautiful" or not pass!

Android Hot fix Framework robust principle + and the framework code from "closed source" into "open source" (the next chapter)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.