Explain in detail how hot updates are implemented in Android

  • 2020-12-18 01:55:11
  • OfStack

This article will introduce the principle of implementing hot update in Android.

1. ClassLoader
We know that Java loads the corresponding class at runtime through ClassLoader. ClassLoader itself is an abstraction. Android uses the PathClassLoader class as the default classloader for Android, and PathClassLoader simply loads the class file from the file system. PathClassLoade itself inherits from BaseDexClassLoader, and BaseDexClassLoader overrides the findClass method, which is at the heart of ClassLoader.


@Override

protected Class> findClass(String name) throws ClassNotFoundException {

  List suppressedExceptions = new ArrayList();

  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;

}

Looking at the source code, BaseDexClassLoader delegates the findClass method to the findClass method of the pathList object. The pathList object is derived from new in the constructor of BaseDexClassLoader, and its type is DexPathList. Take a look at how the ES29en. findClass source code is done:


public Class findClass(String name, List suppressed) {

  for (Element element : dexElements) {

    DexFile dex = element.dexFile;

    if (dex != null) {

      Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);

      if (clazz != null) {

        return clazz;

      }

    }

  }

  if (dexElementsSuppressedExceptions != null) {

    suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));

  }

  return null;

}

You simply iterate over the dexElements list and then load the class by calling the loadClassBinaryName method on the element.dexFile object. If the return value is not null, the class is loaded successfully and the Class object is returned. The dexElements object is initialized in the constructor of the DexPathList class.

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
All makeDexElements does is walk through the dexPath we passed in and then load each dex file once.

2. Implement
Above analyzed Android classes in the loading process, can see DexPathList dexElements list of objects is a core class loading, a class, if can be loaded it dex1 will appear in the dexElements corresponding dex file, and it is also important in dexElements order, appear in front of the dexElements dex will be priority load, 1 denier Class is loaded successfully, will return immediately, that is to say, our hotpatch if you want to do, Make sure that our hotpacth dex file appears before the dexElements list.

To implement hot updates, we need to change PathClassLoader.pathList.dexElements at runtime, and since these properties are private, we need to modify them by reflection. In addition, when constructing the array of dexElements corresponding to our own dex file, we can also take a more tricky way, that is, by constructing an DexClassLoader object to load our dex file, and call dexClassLoader.loadClass (dummyClassName) once.

By inserting dexClassLoader.pathList.dexElements into the default list of ES88en.pathList.dexElements, the system will be given priority to load the classes in dex, thus making hot updates possible.

The following shows part 1 of the code


private static synchronized Boolean injectAboveEqualApiLevel14(

      String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) {

  Log.i(TAG, "--> injectAboveEqualApiLevel14");

  PathClassLoader pathClassLoader = (PathClassLoader) DexInjector.class.getClassLoader();

  DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, nativeLibPath, pathClassLoader);

  try {

    dexClassLoader.loadClass(dummyClassName);

    Object dexElements = combineArray(

        getDexElements(getPathList(pathClassLoader)),

        getDexElements(getPathList(dexClassLoader)));

    Object pathList = getPathList(pathClassLoader);

    setField(pathList, pathList.getClass(), "dexElements", dexElements);

  } catch (Throwable e) {

    e.printStackTrace();

    return false;

  }

  Log.i(TAG, "

The principle of realizing hot renewal in Android is introduced here first. You can consult relevant books for in-depth study and inquiry based on your accumulated knowledge. I hope you can gain something.


Related articles: