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.
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.
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.