Analysis of the principle of Android hot repair

The purpose of this article is to give you an idea of what hotfix is, please contact me for implementation details. After we understand the principle, we can start to write code smoothly. Please remember a word: the paper come zhongjue shallow, must know this to practice. Code needs to be written by ourselves that is their own code, otherwise you read, recite, and finally work still can not write good code






Download more complete project. To be continued. The source code. Github.



You can click on theAbout meContact me for the full PDF


Github.com/xiangjiana/…

What is hot repair

Hot fix: Enables applications to update without reinstalling, helping applications quickly build dynamic repair capabilities.

We usually rush out a release when we get a Bug early on. However, this Bug may be a simple line of code for which iterating a version with full or incremental updates is overkill. Moreover, the popularity of the new version takes time. According to the upgrading habits of Android users, even the relatively active wechat needs more than 10 days to cover 50% of users. Using thermal repair technology, it can cover more than 70% in one day. This is also based on the small size of the patch, which can be downloaded directly over the mobile network.

Hot repair development process:




At present, the Android industry, hot repair technology bloom, major manufacturers have launched their own hot repair solutions, the use of technical solutions are also different. QZone super patch is based on dex subcontracting scheme, and DEX subcontracting is a Java-based ClassLoader mechanism.

This introduction

Any Java program consists of one or more class files. When the program is running, the class files must be loaded to a VIRTUAL machine (VM). The class loading mechanism of Java is responsible for loading these class files. The function of a ClassLoader is simply to load a class file for application runtime. Each Class object has a classLoader field inside it to identify which classLoader loaded it.

class Class<T> { ... private transient ClassLoader classLoader; . }Copy the code

ClassLoader is an abstract class, and its main implementation classes are:

  • BootClassLoader

    Used to load the Android Framework layer class file
  • PathClassLoader

    Class loader for Android applications. Can load the specified dex, as well as jar, zip, apKclasses.dex
  • DexClassLoader

    Used to load the specified dex, as well as jar, zip, apKclasses.dex

Many blogs say that PathClassLoader can only load the dex of the installed APK, but in fact PathClassLoader and DexClassLoader can load the dex of sdcard.

Log.e(TAG, "The Activity. The class by:" + Activity.class.getClassLoader() +"Load");
Log.e(TAG, "MainActivity. Class by:" + MainActivity.class.getClassLoader() +"Load"); 1 / / output: Activity class by: Java. Lang. BootClassLoader @ d3052a9 loading MainActivity. Class by: dalvik.system.PathClassLoader[DexPathList[[zip file"/data/app/com.enjoy.enjoyfix-1/base.apk"], nativeLibraryDirectories [/ data/app/com. Enjoy. / lib/x86 enoyfix - 1, / system/lib/vendor/lib]]]Copy the code

The relationship between them is as follows:






PathClassLoader
DexClassLoader
BaseDexClassLoader

public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath,String optimizedDirectory,  String librarySearchPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory),librarySearchPath, parent);  } } public class PathClassLoader extends BaseDexClassLoader { public PathClassLoader(String dexPath, ClassLoader parent) { super(dexPath, null, null, parent); } public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent){ super(dexPath, null, librarySearchPath, parent); }}Copy the code

The only difference you can see is that creating DexClassLoader requires passing an optimizedDirectory argument, which is created as a File object and passed to super, whereas PathClassLoader is passed directly to NULL. So both can load the specified dex, as well as classes.dex in JAR, ZIP, and APK

PathClassLoader pathClassLoader = new PathClassLoader("/sdcard/xx.dex", getClassLoader());

File dexOutputDir = context.getCodeCacheDir();
DexClassLoader dexClassLoader= new DexClassLoader("/sdcard/xx.dex",dexOutputDir.getAbsolutePath(), null,getClassLoader());
Copy the code

OptimizedDirectory Directory whose parameter is odex. In fact, when the ClassLoader in Android loads dex, it first optimizes dex through dexopt to generate odex files. If the optimizedDirectory is null, the default path is /data/dalvik-cache. And for security reasons, this directory needs to use the app private directory, such as getCodeCacheDir()

DexClassLoader optimizedDirectory is deprecated in API 26.

javapublicDexClassLoader(StringdexPath,StringoptimizedDirectory,StringlibrarySearchPath,ClassLoaderparent){super(dexPath ,null,librarySearchPath,parent); }

Same as PathClassLoader!

Parental delegation mechanism

Creating a ClassLoader requires receiving a ClassLoaderparent parameter. This parent is loaded for the parent class. That is, when a class loader receives a request to load a class, it first delegates the loading task to the parent class loader, recursively. If the parent class loader can complete the class loading task, it returns successfully. Only if the parent class loader is unable to complete the load task, do the load itself. This is the parent delegate mechanism!

protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException{// Check if the class is loaded class c = findLoadedClass(name);if (c == null) {
        long t0 = System.nanoTime();
        try {
            if(parent ! = null) {// If parent is not null, then call the parent loadClass line load c = parent. LoadClass (name,false);
            } elseC = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { }if(c == null) {long t1 = system.nanotime (); c = findClass(name); }}return c;
}
Copy the code

So we create our own ClassLoader: newPathClassLoader(“/sdcard/xx.dex”,getClassLoader()); Not only can you get classes in xx.dex, but you can also get classes loaded in its parent ClassLoader.

findClass

When all the parent ClassLoaders fail to load a Class, they call their own findClass method. FindClass is defined in a ClassLoader as:

protected Class<? > findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }Copy the code

LoadClass and findClass can be overridden by any ClassLoader subclass. Generally, if you don’t want to use parent delegates, override loadClass to change its implementation. Overwriting findClass means defining how to find a Class if both parent classloaders can’t find it. Our PathClassLoader is responsible for loading its own classes in programs like MainActivity, using the parent to delegate the parent ClassLoader to load activities in the Framework. PathClassLoader does not override loadClass, so we can see how findClass is implemented in PathClassLoader.

public BaseDexClassLoader(String dexPath, FileoptimizedDirectory,String librarySearchPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory); } @Override protected Class<? > findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); // Find the specified class c = pathList.findClass(name, suppressedExceptions);if (c == null) {
        ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" +                                                      name + "\" on path: " + pathList);
        for (Throwable t : suppressedExceptions) {
             cnfe.addSuppressed(t);
        }
            throw cnfe;
    }
    return c;
}
Copy the code

The implementation is simple, looking up the class from the pathList. Keep looking at DexPathList

public DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory) { //......... Add (dexPath) // makeDexElements will go to List<File>.add(dexPath) and use DexFile to load the dex File Element array this.dexElements =makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext); / /... } public Class findClass(String name, List<Throwable> suppressed) {// Get a DexFile representing the Dex from elementfor (Element element : dexElements) {
        DexFile dex = element.dexFile;
        if(dex ! = null) {// Find class clazz = dex.loadClassBinaryName(name,definingContext, suppressed);if(clazz ! = null) {returnclazz; }}}if(dexElementsSuppressedExceptions ! = null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); }return null;
}
Copy the code

Hot repair

There is an Element array in the PathClassLoader, and a dexFile member in the Element class represents the dex file. That is, if there are X dex in the APK, there are X elements in the Element array.






for(Elementelement:dexElements)





The Element array in the PathClassLoader is: [patch.dex, class. dex, classes2.dex]. If there is a key. class in patch.dex and classes2.dex, during class search, the loop obtains the DexFile in dexElements, and returns the Key. It does not matter whether dexfiles in subsequent elements can be loaded into key.class.

Therefore, you can make a separate patch.dex file (patch package) for the buggy class. Then, when the program starts, you can download the patch.dex file from the server and save it to a certain path. We then insert the Element object into the dexElements array header in the pathList of our program’s classloader, PathClassLoader. In this way, when loading the buggy class, the repair class in patch.dex will be loaded first to solve the Bug. This is how the qzone hotfix works, using reflection to Hook the dexElements array of the pathList in the PathClassLoader.

conclusion

In order to implement hot fixes, you must master the techniques of reflection, class loading, and the ClassLoader section of the Framewrok layer source code. Gradle programming is also required if you want to continue automatic patch generation.

Each section of the article contains a series of BAT interview surface pilots, these points build a complete knowledge system, hot repair. Later, I will refine the inside of the knowledge, if you feel any questions, please contact me ~

Please see the full PDF version (more complete project downloads). To be continued. The source code. Github. You can click about me to contact me for the full PDF github.com/xiangjiana/…