Android源码学习——ClasLoader(3)
2017-08-06 22:30
453 查看
本文学习的源码参考AndroidXRef,版本为Lollipop 5.1.0_r1。
前面我们讲了ClassLoader的构造函数是怎么实现的,现在来看下具体类加载的实现吧。
在Android中,我们使用loadClass方法来加载我们需要的类,但是在PathClassLoader和DexClassLoader,以及他们的父类BaseDexClassLoader中都没有定义loadClass方法,那么最终调用还是父类ClassLoader中的loadClass方法。
有两个方法,
首先调用
没有被加载,则首先去调用parent的
如果父类加载都失败的话,再去调用
这也即是双亲委派机制。
类加载器在加载一个类之前,会首先去检查自己以及自己以上的类加载器是否已经加载了这个类,如果已经加载过,就会直接将类对象返回;如果自己以上的类加载器都没有加载这个类,该类加载器自身才会去加载相应
4000
的类。
这么做的好处是:
1.效率 不会出现类的重复加载问题;
2.安全 已经加载过的类不会再被覆盖。
继续看
还是调用了DexPathList的
遍历所有的DexFile实例,然后调用
这个方法里面调用了
好吧,又调到native函数去了。
首先是创建DexFile对象,然后对传入的类名javaName做类型转化赋值给class_name;
然后根据类名获取类描述符
通过
遍历所有的dex_file,调用
最后去获取ClassLinker,调用它的
前面我们讲了ClassLoader的构造函数是怎么实现的,现在来看下具体类加载的实现吧。
在Android中,我们使用loadClass方法来加载我们需要的类,但是在PathClassLoader和DexClassLoader,以及他们的父类BaseDexClassLoader中都没有定义loadClass方法,那么最终调用还是父类ClassLoader中的loadClass方法。
public Class<?> loadClass(String className) throws ClassNotFoundException { return loadClass(className, false); } protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(className); if (clazz == null) { ClassNotFoundException suppressed = null; try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { suppressed = e; } if (clazz == null) { try { clazz = findClass(className); } catch (ClassNotFoundException e) { e.addSuppressed(suppressed); throw e; } } } return clazz; }
有两个方法,
loadClass(String className)本质上还是调用了
loadClass(String className, boolean resolve),那么看下
loadClass(String className, boolean resolve)的具体实现。
首先调用
findLoadedClass(className)检查该类是否被加载,如果被加载则直接返回对应的Class对象;
没有被加载,则首先去调用parent的
loadClass(className, false)方法,让父类去尝试加载这个类;
如果父类加载都失败的话,再去调用
findClass(className)加载这个类。
这也即是双亲委派机制。
类加载器在加载一个类之前,会首先去检查自己以及自己以上的类加载器是否已经加载了这个类,如果已经加载过,就会直接将类对象返回;如果自己以上的类加载器都没有加载这个类,该类加载器自身才会去加载相应
4000
的类。
这么做的好处是:
1.效率 不会出现类的重复加载问题;
2.安全 已经加载过的类不会再被覆盖。
继续看
findClass的实现,BaseDexClassLoader重写了该方法:
protected 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; }
还是调用了DexPathList的
findClass方法。
public Class findClass(String name, List<Throwable> 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; }
遍历所有的DexFile实例,然后调用
loadClassBinaryName方法一个个尝试能不能加载想要的类。
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) { return defineClass(name, loader, mCookie, suppressed); }
这个方法里面调用了
defineClass,看下defineClass的实现:
private static Class defineClass(String name, ClassLoader loader, long cookie, List<Throwable> suppressed) { Class result = null; try { result = defineClassNative(name, loader, cookie); } catch (NoClassDefFoundError e) { if (suppressed != null) { suppressed.add(e); } } catch (ClassNotFoundException e) { if (suppressed != null) { suppressed.add(e); } } return result; }
好吧,又调到native函数去了。
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jlong cookie) { std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env); if (dex_files == NULL) { VLOG(class_linker) << "Failed to find dex_file"; return NULL; } ScopedUtfChars class_name(env, javaName); if (class_name.c_str() == NULL) { VLOG(class_linker) << "Failed to find class_name"; return NULL; } const std::string descriptor(DotToDescriptor(class_name.c_str())); const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str())); for (const DexFile* dex_file : *dex_files) { const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash); if (dex_class_def != nullptr) { ScopedObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->RegisterDexFile(*dex_file); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader))); mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash, class_loader, *dex_file, *dex_class_def); if (result != nullptr) { VLOG(class_linker) << "DexFile_defineClassNative returning " << result; return soa.AddLocalReference<jclass>(result); } } } VLOG(class_linker) << "Failed to find dex_class_def"; return nullptr; }
首先是创建DexFile对象,然后对传入的类名javaName做类型转化赋值给class_name;
然后根据类名获取类描述符
DotToDescriptor(class_name.c_str()),函数实现是:
std::string DotToDescriptor(const char* class_name) { std::string descriptor(class_name); std::replace(descriptor.begin(), descriptor.end(), '.', '/'); if (descriptor.length() > 0 && descriptor[0] != '[') { descriptor = "L" + descriptor + ";"; } return descriptor; }
通过
ComputeModifiedUtf8Hash(descriptor.c_str())计算该类的hash索引;
遍历所有的dex_file,调用
FindClassDef(descriptor.c_str(), hash)去获得这个类的ClassDef;
最后去获取ClassLinker,调用它的
RegisterDexFile注册dex_file,并且调用
DefineClass完成类的加载。
相关文章推荐
- Android源码学习之一-Activity是如何实现主题变化的
- Android源码学习之二-Activity如何管理对话框
- Android源码学习之六——ActivityManager框架解析
- Android2.1消息应用(Messaging)源码学习笔记(转载)
- Android Animation学习笔记--附带源码
- Android源码学习之七—传感器的背后
- Android源码学习之一-Activity是如何实现主题变化的
- android源码学习之animation1
- Android源码学习之七—传感器的背后
- Android2.1消息应用(Messaging)源码学习笔记
- Android2.1消息应用(Messaging)源码学习笔记(转载)
- 快乐分享Android学习心得---如何在Eclipse中看Android2.2的源码
- Android2.1消息应用(Messaging)源码学习笔记
- (转)Android Animation学习笔记--附带源码
- Android源码学习之一-Activity是如何实现主题变化的
- Android2.1消息应用(Messaging)源码学习笔记(转载)
- Android源码学习之三-Activity是如何进行自动化测试的
- Android源码学习之八—系统启动过程
- Android源码学习之三-Activity是如何进行自动化测试的
- 通过创建一个位图的XY Chart来学习Android绘图类Rect,Paint,Bitmap,Canvas(附源码)