您的位置:首页 > 移动开发 > Android开发

Android源码学习——ClasLoader(3)

2017-08-06 22:30 453 查看
本文学习的源码参考AndroidXRef,版本为Lollipop 5.1.0_r1。

前面我们讲了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
完成类的加载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: