Mobile Development 11 min read

Detailed Analysis of Android ClassLoader Loading Mechanism

This article provides an in‑depth examination of Android's ClassLoader architecture, explaining how BaseDexClassLoader.findClass() and ClassLoader.loadClass() locate and load classes, with step‑by‑step source code analysis of loadClass, findLoadedClass, findClass, DexPathList, DexFile, and native class definition processes.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Detailed Analysis of Android ClassLoader Loading Mechanism

Android developers frequently encounter dynamic class loading, which relies heavily on the ClassLoader hierarchy. This article analyzes the source code of Android's ClassLoader to clarify the underlying principles of dynamic loading.

Detailed analysis of ClassLoader loading principle

The inheritance chain of ClassLoader is illustrated, focusing on the functions BaseDexClassLoader.findClass() and ClassLoader.loadClass() . The implementation of ClassLoader.loadClass() in ClassLoader.java is shown below:

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) {
        try {
            if (parent != null) {
                // Delegate to parent ClassLoader
                c = parent.loadClass(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 not found, use findClass in the current dex
            c = findClass(name);
        }
    }
    return c;
}

protected Class
findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

The loading steps are:

1. loadClass() first calls findLoadedClass() to see if the class is already loaded.

2. If not found, it recursively checks the parent ClassLoader.

3. If still not found, it searches the BootClassLoader .

4. Finally, it calls findClass() to look for the class in the current dex.

findLoadedClass() function analysis

The call flow of findLoadedClass() is illustrated, and its source code is:

protected final Class
findLoadedClass(String name) {
    ClassLoader loader;
    if (this == BootClassLoader.getInstance())
        loader = null;
    else
        loader = this;
    return VMClassLoader.findLoadedClass(loader, name);
}

The native method VMClassLoader.findLoadedClass() is implemented in java_lang_VMClassLoader.cc :

native static Class findLoadedClass(ClassLoader cl, String name);

Further analysis shows that findLoadedClass() performs two steps:

1. Uses class_linker_->LookupClass() to search the class.

2. If not found, calls class_linker_->FindClassInPathClassLoader() for another lookup.

findClass() function analysis

The findClass() implementation in BaseDexClassLoader.java searches the current dex:

@Override
protected Class
findClass(String name) throws ClassNotFoundException {
    List
suppressedExceptions = new ArrayList<>();
    Class c = pathList.findClass(name, suppressedExceptions);
    if (c == null) {
        // ... handle failure
        throw cnfe;
    }
    return c;
}

The DexPathList holds dexElements representing dex file handles. Its constructor creates these elements via makeDexElements() , which ultimately calls DexFile.loadDex() to parse and load dex files:

private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements) throws IOException {
    if (optimizedDirectory == null) {
        return new DexFile(file, loader, elements);
    } else {
        String optimizedPath = optimizedPathFor(file, optimizedDirectory);
        return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
    }
}

The native method openDexFileNative() in dalvik_system_DexFile.cc opens the dex file and registers it with the runtime:

static jobject DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName,
                                         jstring javaOutputName, jint flags,
                                         jobject class_loader, jobjectArray dex_elements) {
    // ... obtain Runtime and ClassLinker
    dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(...);
    // ...
}

When findClass() traverses dexElements , each element invokes loadClassBinaryName() which eventually calls the native defineClassNative() to define the class in the VM:

private static native Class defineClassNative(String name, ClassLoader loader,
                                             Object cookie, DexFile dexFile);

The native implementation in dalvik_system_DexFile.cc uses the ClassLinker to define the class and insert the dex file into the class table for caching.

In summary, the Android ClassLoader loading process involves a hierarchical delegation model, cache checks via findLoadedClass() , fallback to the boot class loader, and finally dex‑file lookup and native class definition. Understanding these steps helps developers grasp dynamic loading behavior in Android.

For deeper insight, readers are encouraged to explore the referenced source files directly.

JavaAndroidruntimeClassLoaderDexDynamicLoading
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.