Android类加载流程

背景由于前前前阵子写了个壳,得去了解类的加载流程,当时记了一些潦草的笔记 。这几天把这些东西简单梳理了一下 , 本文分析的代码基于Android8.1.0源码 。
流程分析从loadClass开始,我们来看下Android中类加载的流程

/libcore/ojluni/src/main/java/java/lang/ClassLoader.java::loadClass
loadClass流程如下:
Android类加载流程

文章插图
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {try {if (parent != null) {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 still not found, then invoke findClass in order// to find the class.c = findClass(name);}}return c;}
/libcore/ojluni/src/main/java/java/lang/ClassLoader.java::findClass
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}ClassLoader类的findClass是没有实际查找代码的,所以调用findClass其实是调用其实现类的findClass函数 , 例如:BaseDexClassLoader
/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java::findClass
每个BaseDexClassLoader都持有一个DexPathList,BaseDexClassLoader的findClass类调用了DexPathList的findClass 。
@Overrideprotected 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;}
/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java::findClass
遍历所有dexElements,并调用Element类的findClass 。
public Class<?> findClass(String name, List<Throwable> suppressed) {for (Element element : dexElements) {Class<?> clazz = element.findClass(name, definingContext, suppressed);if (clazz != null) {return clazz;}}if (dexElementsSuppressedExceptions != null) {suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));}return null;}题外话,dexElements对象其实是DexPathList$Element类的数组 , 用于存储已加载的dex或者jar的信息 。
/libcore/dalvik/src/main/java/dalvik/system/DexPathList$Element::findClass
Element的findClass,又去调用DexFile类的loadClassBinaryName,可以理解为在单独的dex或者jar对象中加载类
public Class<?> findClass(String name, ClassLoader definingContext,List<Throwable> suppressed) {return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed): null;}
libcore\dalvik\src\main\java\dalvik\system\DexFile.java::loadClassBinaryName
去调用defineClass函数
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {return defineClass(name, loader, mCookie, this, suppressed);}
libcore\dalvik\src\main\java\dalvik\system\DexFile.java::defineClass
调用defineClassNative , 准备进入Native层
private static Class defineClass(String name, ClassLoader loader, Object cookie,DexFile dexFile, List<Throwable> suppressed) {Class result = null;try {result = defineClassNative(name, loader, cookie, dexFile);} catch (NoClassDefFoundError e) {if (suppressed != null) {suppressed.add(e);}} catch (ClassNotFoundException e) {if (suppressed != null) {suppressed.add(e);}}return result;}
art\runtime\native\dalvik_system_DexFile.cc::DexFile_defineClassNative
检查dex是否加载 , 类名是否合理,并遍历DexFile对象 , 查找Dex文件中的类的定义,找到就去调用ClassLinker::DefineClass函数 。
static jclass DexFile_defineClassNative(JNIEnv* env,jclass,jstring javaName,jobject javaLoader,jobject cookie,jobject dexFile) {std::vector<const DexFile*> dex_files;const OatFile* oat_file;if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {VLOG(class_linker) << "Failed to find dex_file";DCHECK(env->ExceptionCheck());return nullptr;}ScopedUtfChars class_name(env, javaName);if (class_name.c_str() == nullptr) {VLOG(class_linker) << "Failed to find class_name";return nullptr;}const std::string descriptor(DotToDescriptor(class_name.c_str()));const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));for (auto& dex_file : dex_files) {const DexFile::ClassDef* dex_class_def =OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);if (dex_class_def != nullptr) {ScopedObjectAccess soa(env);ClassLinker* class_linker = Runtime::Current()->GetClassLinker();StackHandleScope<1> hs(soa.Self());Handle<mirror::ClassLoader> class_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));ObjPtr<mirror::DexCache> dex_cache =class_linker->RegisterDexFile(*dex_file, class_loader.Get());if (dex_cache == nullptr) {// OOME or InternalError (dexFile already registered with a different class loader).soa.Self()->AssertPendingException();return nullptr;}ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),descriptor.c_str(),hash,class_loader,*dex_file,*dex_class_def);// Add the used dex file. This only required for the DexFile.loadClass API since normal// class loaders already keep their dex files live.class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),class_loader.Get());if (result != nullptr) {VLOG(class_linker) << "DexFile_defineClassNative returning " << result<< " for " << class_name.c_str();return soa.AddLocalReference<jclass>(result);}}}VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();return nullptr;}

推荐阅读