SoFunction
Updated on 2025-03-08

Detailed explanation of Android class loading ClassLoader

Basic knowledge

Java's class loading designs a set of parent proxy mode, which makes users unable to replace the core classes of the system, making the application more secure. The so-called parent proxy means that when loading the class, first loading the class in Bootstrap, if not, loading it in Extension, if not, loading it in AppClassLoader. This will achieve safety and stability.

Java ClassLoader

BootstrapClassLoader

Boot class loader, used to load the core library of Java. Implemented through the underlying code, basically as long as parent is null, it means the boot class loader.

for example:,,,,,,,,,,

ExtClassLoader

Extended class loader, used to load Java's extended class library, all jars in the ${JAVA_HOME}/jre/lib/ext/ directory.

For example:,,,,,,,,,,,,,,,,,,,,,,,,,,,,

AppClassLoader

System class loader (don't be confused by the name) is used to load classes in Java applications. Generally speaking, the classes you write are loaded through this. In Java () returns AppClassLoader. (The logic of ClassLoader has been modified in Android, and the return is a PathClassLoader)

Custom ClassLoader

If users want to customize ClassLoader, they only need to inherit from it.

Methods related to loading classes in ClassLoader:

  1. getParent() returns the parent class loader of the class loader.
  2. loadClass(String name) Loads a class with name name, and the result returned is an instance of the class.
  3. findClass(String name) Finds a class with name name, and the result returned is an instance of the class.
  4. findLoadedClass(String name) Finds a loaded class with the name name, and the result returned is an instance of the class.
  5. defineClass(String name, byte[] b, int off, int len) Converts the contents in byte array b into a Java class, and the result returned is an instance of the class. This method is declared final.

Maybe you don’t know much about the differences between the above functions, it doesn’t matter. Let’s take a look at how the source code is implemented.

//
protected Class<?> loadClass(String name, boolean resolve)
  throws ClassNotFoundException
{
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
      long t0 = ();
      try {
        if (parent != null) {
          c = (name, false);
        } else {
          c = findBootstrapClassOrNull(name);
        }
      } catch (ClassNotFoundException e) {
        // ClassNotFoundException thrown if class not found
        // from the non-null parent class loader
      }

      if (c == null) {
        // If still not found, then invoke findClass in order
        // to find the class.
        long t1 = ();
        c = findClass(name);

        // this is the defining class loader; record the stats
      }
    }
    return c;
}

So the priority is roughly as follows:

loadClass → findLoadedClass → /findBootstrapClassOrNull → findClass → defineClass

Android ClassLoader

In Android, ClassLoader has two direct subclasses, called BaseDexClassLoader and SecureClassLoader. The former has two direct subclasses: PathClassLoader and DexClassLoader (Android O adds InMemoryDexClassLoader, omitted).

We only discuss PathClassLoader and DexClassLoader

PathClassLoader

Used to load the dex file in the installed application. It is also the most core ClassLoader in Android. It is equivalent to the AppClassLoader in Java.

public class PathClassLoader extends BaseDexClassLoader {
  /**
   * Creates a {@code PathClassLoader} that operates on a given list of files
   * and directories. This method is equivalent to calling
   * {@link #PathClassLoader(String, String, ClassLoader)} with a
   * {@code null} value for the second argument (see description there).
   *
   * @param dexPath the list of jar/apk files containing classes and
   * resources, delimited by {@code }, which
   * defaults to {@code ":"} on Android
   * @param parent the parent class loader
   */
  public PathClassLoader(String dexPath, ClassLoader parent) {
    super(dexPath, null, null, parent);
  }

  /**
   * Creates a {@code PathClassLoader} that operates on two given
   * lists of files and directories. The entries of the first list
   * should be one of the following:
   *
   * <ul>
   * <li>JAR/ZIP/APK files, possibly containing a "" file as
   * well as arbitrary resources.
   * <li>Raw ".dex" files (not inside a zip file).
   * </ul>
   *
   * The entries of the second list should be directories containing
   * native library files.
   *
   * @param dexPath the list of jar/apk files containing classes and
   * resources, delimited by {@code }, which
   * defaults to {@code ":"} on Android
   * @param librarySearchPath the list of directories containing native
   * libraries, delimited by {@code }; may be
   * {@code null}
   * @param parent the parent class loader
   */
  public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
    super(dexPath, null, librarySearchPath, parent);
  }
}

Its instantiation is by calling To achieve it.

It calls LoadedApk when ActivityThread is started and then sends a BIND_APPLICATION message. getResources(ActivityThread mainThread) Finally, I went back to ActivityThread and called LoadedApkgetClassLoaderGenerated, specific in LoadedApk createOrUpdateClassLoaderLocked

So the question is, when Android loads class, how is the ClassLoader in LoadedApk called?

In fact, if you don't give ClassLoader in Class, it will default to get the CallingClassLoader in the Java virtual machine stack, and this is the same ClassLoader in LoadedApk.

//
public static Class<?> forName(String className)
      throws ClassNotFoundException {
  return forName(className, true, ());
}

Check the source code of VMStack getCallingClassLoader It is actually a native function, and Android implements this through the underlying layer.

//
/**
 * Returns the defining class loader of the caller's caller.
 *
 * @return the requested class loader, or {@code null} if this is the
 *     bootstrap class loader.
 */
@FastNative
native public static ClassLoader getCallingClassLoader();

The bottom layer must eventually get the ClassLoader in LoadedApk.

DexClassLoader

It is a jar or apk file that contains a dex file, but it can be used to load non-installed apks. For example, load the sdcard or NetWork.

public class DexClassLoader extends BaseDexClassLoader {
  /**
   * Creates a {@code DexClassLoader} that finds interpreted and native
   * code. Interpreted classes are found in a set of DEX files contained
   * in Jar or APK files.
   *
   * <p>The path lists are separated using the character specified by the
   * {@code } system property, which defaults to {@code :}.
   *
   * @param dexPath the list of jar/apk files containing classes and
   *   resources, delimited by {@code }, which
   *   defaults to {@code ":"} on Android
   * @param optimizedDirectory directory where optimized dex files
   *   should be written; must not be {@code null}
   * @param librarySearchPath the list of directories containing native
   *   libraries, delimited by {@code }; may be
   *   {@code null}
   * @param parent the parent class loader
   */
  public DexClassLoader(String dexPath, String optimizedDirectory,
      String librarySearchPath, ClassLoader parent) {
    super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
  }
}

For example, the popular plug-in/hot patches are actually implemented through DexClassLoader. The specific idea is: Create a DexClassLoader, merge the former DexPathList with the system's PathClassLoader by reflection, and then we can prioritize loading our own new class, thereby replacing the logic in the old class.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.