Android hot fix is as simple as it used to be

1, What is hot repair

Hot fix is simply "patching". For example, when your company launches an app, users respond that there are major bug s and need to be repaired urgently. If according to
The usual practice is to fix the bug, test, repackage and release. The problem brought by this is high cost and low efficiency. So, heat
Generally, the Bug free code is downloaded from the Internet through a preset interface to replace the Bug free code. It'll save you a lot of trouble
The customer experience is good.

2, Principle of thermal repair

1.Android class loading mechanism

There are two types of class loaders for Android, PathClassLoader and DexClassLoader, both of which inherit from BaseDexClassLoader

The PathClassLoader code is located in libcore\dalvik\src\main\java\dalvik\system\PathClassLoader.java
The DexClassLoader code is located in libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java
The BaseDexClassLoader code is located in libcore\dalvik\src\main\java\dalvik\system\BaseDexClassLoader.java

PathClassLoader
Used to load system classes and application classes

DexClassLoader

It is used to load jar, apk and DEX files. Loading jar and apk is also the final extraction of DEX files for loading

2. Thermal repair mechanism

Take a look at the PathClassLoader code

public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
} 

DexClassLoader code

public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

Two classloaders are just two or three lines of code, just calling the constructor of the parent class

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        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;
    }

Create an instance of the DexPathList class in the BaseDexClassLoader constructor, and the DexPathList constructor will create a dexElements array

public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
        ... 
        this.definingContext = definingContext;
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        //Create an array
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
        ... 
    }

Then BaseDexClassLoader rewrites the findClass method, calls pathList.findClass, and jumps to the DexPathList class

/* package */final class DexPathList {
    ...
    public Class findClass(String name, List<Throwable> suppressed) {
            //Traverse the array
        for (Element element : dexElements) {
            //Initialize DexFile
            DexFile dex = element.dexFile;

            if (dex != null) {
                //Call the loadClassBinaryName method of the DexFile Class to return the Class instance
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }       
        return null;
    }
    ...
} 

It will traverse the array and initialize DexFile. If DexFile is not empty, call the loadClassBinaryName method of DexFile Class to return the Class instance
To sum up, ClassLoader will traverse the array and then load the dex file in the array
After loading the correct class, ClassLoader will not load the class with Bug. We can put the correct class in the Dex file and put the Dex file in front of the dexElements array

Here is a problem. Please refer to the introduction of Android App hot patch dynamic repair technology of QQ space team
To sum up: if the classes (direct reference relationship) of the referent and the referenced are in the same Dex, the referenced class will be marked with class when the virtual machine is started_ Isprevified flag, so that the referenced class cannot be hot repaired
Then we must prevent the referenced class from being labeled class_ Isprevified flag. The method of QQ space is to insert a piece of code into all constructors that refer to this class, and the code refers to other classes

3, Examples of thermal repair

I use the open source hot repair framework of Alibaba and fix hot repair framework address

In fact, its principle is to load class files dynamically, and then call reflection to complete the repair.

AndFix is the abbreviation of "Android hot fix". It supports Android 2.3 to 6.0, and supports devices with arm and X86 system architecture. Perfect support for Dalvik and ART Runtime. The patch file for AndFix is a file ending in. apatch.

This is a Demo written in eclipse

1. Extract AndFix into the form of library dependency

2. Create a new AndFixDemo project and rely on the library of AndFix

2.1
Create a new MyApplication inheritance Application

public class MyApplication extends Application {

    private static final String TAG = "MyApplication";

    /**
     * apatch file
     */
    private static final String APATCH_PATH = "/Dennis.apatch";

    private PatchManager mPatchManager;

    @Override
    public void onCreate() {
        super.onCreate();
        // initialization
        mPatchManager = new PatchManager(this);
        mPatchManager.init("1.0"); // Version number

        // Load apatch
        mPatchManager.loadPatch();

        //Directory of apatch files
        String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;
        File apatchPath = new File(patchFileString);

        if (apatchPath.exists()) {
            Log.i(TAG, "Patch file exists");
            try {
                //Add apatch file
                mPatchManager.addPatch(patchFileString);
            } catch (IOException e) {
                Log.i(TAG, "Patching error");
                e.printStackTrace();
            }
        } else {
            Log.i(TAG, "Patch file does not exist");
        }

    }
}

In fact, the apatch file must be downloaded through the network interface. I put it in the root directory of the SD card for the convenience of demonstration

2.2
Use a button to pop up toast in MainActivity. The code with Bug is above and the revised code is below
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-whdo4cyt-1635561199273)( https://upload-images.jianshu.io/upload_images/27187314-0b364261fbd61bad.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240)]

Package into Bug.apk and NoBug.apk respectively**

2.3
Then you need to use a patch generation tool apkpatch

decompression

_MACOSX is for OSX system
. bat is for window system

I use. bat

Put the previously generated Bug.apk, NoBug.apk, and the keystore file used for packaging into the apkpatch-1.0.3 directory
Open cmd, enter apkpatch-1.0.3 directory, and enter the following command

apkpatch.bat -f NoBug.apk -t Bug.apk -o Dennis -k keystore -p 111111 -a 111111 -e 111111

The meaning of each parameter is as follows

-f new version of apk
-t old version of apk
-o folder for outputting apatch files, which can be named at will
-k packaged keystore file name
-Password for p keystore
-a keystore user alias
-e keystore password for user alias

If add modified... Appears, it means success. Go to apkpatch-1.0.3 directory and add Dennis directory

I changed this file to Dennis.apatch

2.4

Install Bug.apk on the mobile phone and run it

Then put Dennis.apatch in the root directory of the SD card, exit the app, enter again, and press the button


Finally, the Demo and apk and apatch files are attached Open link

This article is transferred from https://blog.csdn.net/qq_31530015/article/details/51785228 , in case of infringement, please contact to delete.

Relevant video recommendations:

Android performance optimization learning [2]: APP startup speed optimization
[Android advanced system learning]: bytecode stake insertion technology to realize automation, time-consuming recording bilibili bili
Android performance optimization learning [2]: APP startup speed optimization
[Android interview topic]: the interview was asked about interprocess communication, but you don't even know what Binder is? Beep beep bilibili bili
BAT interview skills - what do you know about network programming for Android interview? Beep beep bilibili bili

Posted on Sat, 30 Oct 2021 03:04:16 -0400 by gordonmc