One, foreword

There are many hot fix frameworks in Android, and each company has corresponding solutions and frameworks, such as AndFix framework of Ali. About this framework has been explained in detail in the previous article, and students who do not know can click here: AndFix hot fix framework principle analysis. This paper continues to look at another hot repair framework, namely the Robust framework developed by Meituan team. This framework is explained in detail on the web, as well as its usage. However, he did not open source, so this article will briefly introduce his principle, with a case to demonstrate the role of this framework, but the focus is how we can code their framework mechanism implementation, so that its “closed source” into “open source”.



Two, principle analysis

As for hot repair technology, although each family has corresponding framework, the core point is inseparable from the dynamic loading mechanism. With the dynamic loading mechanism, then it is the problem of the specific repair scheme, and the repair scheme for the Robust is relatively simple. Here’s a quick look at his rationale:

The Robust plug-in automatically inserts a code for each function of each product code in the compilation and packaging stage, and the insertion process is completely transparent to business development. For example, the getIndex function in state.java:



public long getIndex() {  
    return 100;  
} Copy the code 

Is processed into the following implementation:

public static ChangeQuickRedirect changeQuickRedirect; public long getIndex() { if(changeQuickRedirect ! = null) {//PatchProxy encapsulates the logic to get the current className and methodName, IsSupport (New Object[0], this, changeQuickRedirect, false)) { return ((Long)PatchProxy.accessDispatch(new Object[0], this, changeQuickRedirect, false)).longValue(); } } return 100L; }Copy the code


The Robust adds a static member of type ChangeQuickRedirect to each class, and uses ChangeQuickRedirect logic in front of each method. If ChangeQuickRedirect is not null, the Robust adds a static member of type ChangeQuickRedirect to each class. It is possible to execute to accessDispatch to replace the old logic for fix purposes. If the return value of getIndex is changed to return 106, the generated patch contains two classes: PatchesInfoImp. Java and Statepatch.java.

public class PatchesInfoImpl implements PatchesInfo { public List getPatchedClassesInfo() { List patchedClassesInfos = new ArrayList(); PatchedClassInfo patchedClass = new PatchedClassInfo("com.meituan.sample.d", StatePatch.class.getCanonicalName()); patchedClassesInfos.add(patchedClass); return patchedClassesInfos; }}Copy the code

StatePatch. Java:

public class StatePatch implements ChangeQuickRedirect { @Override public Object accessDispatch(String methodSignature, Object[] paramArrayOfObject) { String[] signature = methodSignature.split(":"); if (TextUtils.equals(signature[1], "a")) {//long getIndex() -> a return 106; } return null; } @Override public boolean isSupport(String methodSignature, Object[] paramArrayOfObject) { String[] signature = methodSignature.split(":"); if (TextUtils.equals(signature[1], "a")) {//long getIndex() -> a return true; } return false; }}Copy the code


After the client gets patch.dex containing PatchesInfoPl. Java and Statepatch. Java, it loads the patch.dex with DexClassLoader and gets the patchesInfoPl. Java class by reflection. Once you get it, create an object for the class. Then, through the getPatchedClassesInfo function of this object, we know that the class to be patched is com.meituan.sample.d (the confused name of com.meituan.sample.State). Then reflect the com.meituan.sample.d class in the current operating environment and assign the changeQuickRedirect field value to the object derived from the statepatch. Java class new in patch.dex. This is the main process of patch. Through principle analysis, the Robust is only using DexClassLoader normally, so it can be said that this framework has no compatibility problems.



Here is a picture to see the structure of the family (click download to view the large hd picture) :





Take a look at meituan Team’s public schematics:



The principle is really simple: load the fix package directly with DexClassLoader, then load the fix class with loadClass method, new the new fix object, and use reflection to set the new fix object into the changeQuickRedirect static variable of the specified class. This fix is similar to the static proxy of design patterns in Java.



Iii. Case practice

Knowing the general principle, let’s use a simple case to run, see the effect, here we need to create three new projects, directly copy the previous development of the plug-in project, you can see this article: Android plug-in development principle analysis, three project diagram structure is as follows:



This paper still adopts this structure diagram as follows:



RobustHost, RobustPatch, RobustPatchImpl, RobustInsertCodeTools, RobustInsertCodeTools, RobustInsertCodeTools, RobustInsertCodeTools.

RobustPatch repair package interface engineering



This project is relatively simple, most of which are interface definitions. There are three main classes and interfaces:

1. ChangeQuickRedirect Interface



This interface is mainly an interface that each repair class in the repair pack must implement. There are two internal methods: one is to determine whether the current method supports the repair, and the other method is the specific repair logic.

The arguments to both methods are the same:

The first parameter is the signature of the method. The format of the signature is simple: full name of the method: name of the method: whether the method is static.

The second parameter is the method parameter information, and for this parameter after the analysis of dynamic insertion code logic will find that the operation is very troublesome, only to get this parameter.



2. PatchesInfo interface



This interface is relatively simple, with only one method, and the user stores the details of all classes that need to be fixed in a fix pack, because there may be multiple classes that need to be fixed in a fix pack. This interface is also used for dynamic loading in later host projects. This interface is used to get the specified fixed class and old class information.



3, PatchedClassInfo class



This class is relatively simple, mainly is to store two fields: one is the repair class information, one is to repair the old class information, so that this class will be used by the above interface.



Second, repair package project

This is the end of the repair pack interface project. Here’s a look at the repair kit project:



This class must implement the ChangeQuickRedirect interface in the fix pack interface project, and then save the information about the fix class and the old class to be fixed. Saved in PatchesInfoImpl, which implements the PatchesInfo interface in the fix pack interface project.

1. MoneyBeanStatePatch repair class



This class sees it, implements the fix interface ChangeQuickRedirect, and then implements two specific methods.

1. In the isSupport method, the method name will be obtained through the method signature information, and then the method supporting repair will be judged. What you see here is that the getMoneyValue and desc methods in the class to be fixed need to be fixed.

In accessDispatch method, the specific repair scheme was mainly implemented. At the beginning, the method name was obtained through method signature information, and then the method name was determined to be repaired. Here, the return value of getMoneyValue method was repaired to 10000. Fix the return value of desc to “Patch desc “.



Third, host project RobustHost



One of the biggest tasks of the host project is to load the fix pack, which needs no explanation but to be placed in the Application. There’s nothing to be said for loading logic. Here we have a MoneyBean class:



Each method of this class is preceded by a code snippet with the ability to fix it. For example, if there is a problem with the return value of this class’s method in the package online, then you can use the hotfix feature. Just pack up the second fix pack project and release it.

Again, there is a PatchProxy class:



This class is a wrapper around the repair class. There is an important logic inside that is to get the information of the current executing method, including the class name and method name, which is mainly obtained through the method stack information:



Then there is the logic for loading the fix pack in the host project:



Use DexClassLoader to load the fix pack file, which is not explained much. With the loader in place, the subsequent loading logic begins. First we need to get the PatchesInfoImpl class information, because this class holds the repair class information. So new produces a new object. Then we call its method to get the trust information of the fixed class, and iterate through the list of fixed class information to get the information of the fixed class and the old class to be fixed. We still use reflection new object, and then set the fixed class object to the static variable changeQuickRedirect of the old class to be fixed.



Four, the operation effect

Well, here we have finished the analysis of the projects involved in the Robust thermal repair framework, mainly three projects. Now I’m going to run the results directly. RobustPatchImpl, RobustPatchImpl, RobustPatchImpl, RobustPatchImpl, RobustPatchImpl, RobustPatchImpl Change it to patch.dex and place it in the SD card directory of the device. An error may occur when running:



There is no more explanation for this error here, because we added the code for the interface project to the fix pack dex. For details on the cause of the error and how to resolve it, you must go here: Android plug-in development principles. I’ll be sure to remember.

Assuming we’ve solved all the problems, let’s run this to see what happens:



Here’s a look at the effect before the repair:



From here, we can see that the repair method is successful.



5. Analyze practical cases of Meituan

However, we haven’t finished our work yet, because the framework of Meituan is not open source, so the above case is not persuasive. So in order to verify that our implementation logic is correct, we need to decompile the Meituan app to see what its hotfix logic looks like. Here you can directly use Jadx tool to open meituan app:



After we open the app, we can directly search the PatchProxy Class globally. Remember to check the Class, otherwise there will be too much content. Then I found the definition of this class:



Seeing the implementation of this class, I actually want to tell you that the PatchProxy class in my case project is exported from this class, because I am too lazy to write the code. Fortunately, this class is not confused.

As we continue to look at his loading code logic, here is a small trick:When viewing dynamically loaded logic in an application, you can search globally for information: DexClassLoader,The results are as follows:



PatchExecutor: PatchExecutor: PatchExecutor: PatchExecutor



Look at the core load logic here, without much explanation, the code logic is very simple, roughly the same as what we implemented. Finally, let’s see if each method in the app class inserts a fix snippet:



Open up any class, and every method in the class will be preceded by this fix code. Ha ha, by now we are very sure that our case implementation and meituan’s repair framework implementation logic is roughly the same.



Automatic insertion of repair code

In fact, this is not the end, because this article is actually not my intention to introduce the Robust framework, because after reading the above principle, you find that it is not difficult to repair the principle. My idea is how to get every method in every class to insert a fix.

Because as we can see from the above, the biggest difficulty of the framework is that there must be a fix section before each method of each class. If there is no such method, the repair function is lost. The question is, how can a large project like Meituan ensure that every developer writing a method has to manually add this fix code? It’s impossible to constrain success. This requires automation. In fact, the framework’s official description of the framework states that “insertion code is automated at compile time and transparent to developers”. So that was the most seductive sentence for me, because I studied the framework for that sentence. Therefore, limited to space reasons, can not be explained in one article. As you can see, the core of the Robust framework is not in this article, but in the next one, where I will show you how to manually insert fix code at build time without the developer having to worry about it. After the introduction of that article, the advantages and disadvantages of meituan’s Robust thermal repair framework will be introduced in detail.



We inform you that

The advantages and disadvantages of meituan’s Robust hot repair framework will be summarized after the automatic code insertion function is introduced in the next paper. Again, the focus of the framework is on automatic insertion of code modules, which may be one reason why Meituan is not open source, but there are others. The practical cases in this paper have roughly realized the Robust framework principle, but there are still many details that need to be optimized. As can be seen from the code of my three projects, the writing is rather rough. A lot of things are not taken into account. So if you want to improve their own optimization, you can operate on your own.





Project download address: github.com/fourbrother…



Seven,

This paper mainly introduces the Robust principle of Meituan’s hot repair framework. Since it is not open source, we have roughly realized a simple case by analyzing the official principle. In order to verify that our case is similar to the framework logic of Meituan APP, we have decomcompiled the Meituan app. After checking, it is confirmed that our implementation logic is roughly the same as the Robust implementation, and the specific details are not mentioned. However, one of the major constraints of this framework is that every method of each class must be preceded by a fix code. If this method is not added, the fix will be lost. Of course, it’s impossible to force every developer to add this code manually during development. The official explanation is that it is automatically added during project compilation and is transparent to developers. So that’s the focus of my parsing of the framework this time, and the focus of the next one, how to write tools to implement auto-insert fix code. Here I feel very guilty, originally wanted to in this article together all explained over, but did not think of the article to write to write more content, really can not be introduced in an article, can only be introduced. However, this does not affect the focus of the next chapter, I hope you can finish reading this chapter to maintain a high level of enthusiasm for the next chapter.