Android ClassLoader详解
来源:互联网 发布:三菱fx5u编程手册 编辑:程序博客网 时间:2024/06/07 18:34
Classloader概述:
Classloader种类,如下图:(原图链接)
一般而言 :我们一个app至少要用到PathClassLoader和BootClassLoader;
特点和作用:
特点
loadClass方法在加载一个类的实例的时候,
- 会先查询当前ClassLoader实例是否加载过此类,有就返回;
- 如果没有。查询Parent是否已经加载过此类,如果已经加载过,就直接返回Parent加载的类;
- 如果继承路线上的ClassLoader都没有加载,才由Child执行类的加载工作;
这样做有个明显的特点,如果一个类被位于树根的ClassLoader加载过,那么在以后整个系统的生命周期内,这个类永远不会被重新加载。
这也是我们经常说的双亲委托机制。
作用
共享功能:一些Framework层级的类一旦被顶层的ClassLoader加载过就缓存在内存里面,以后任何地方用到都不需要重新加载。
隔离功能:不同继承路线上的ClassLoader加载的类肯定不是同一个类,这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。这也好理解,一些系统层级的类会在系统初始化的时候被加载,比如java.lang.String,如果在一个应用里面能够简单地用自定义的String类把这个系统的String类给替换掉,那将会有严重的安全问题。
最后,这也带来了一个新的认知:
之前我们可能觉的: 同一个Class = 相同的 ClassName + PackageName
现在我们应该知道: 同一个Class = 相同的 ClassName + PackageName + ClassLoader
源码分析
大概了解了calssloader之后,我们从源码的角度来看一看classloader的具体实现,首先我们进入classloader的核心方法来
ClassLoader#loadclass()
看看
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { //从缓存中查找是否加载过这个类 Class<?> clazz = findLoadedClass(className); if (clazz == null) { ClassNotFoundException suppressed = null; try { //缓存中没有,我们调用父类来加载(这里递归上去就会让最顶层的classloader来加载) 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; }
从上面的源码,我们也很清楚的知道了什么是双亲委托机制把。。。
了解了双亲委托机制之后,我们再来看看到底是怎么加载类的,从上面源码知道,classloader真正实现加载类的方法是再
findClass()
中的,那么我们去看看BaseDexClassLoader#findClass()
:
@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); //从这里我们看到,最后是交给了pathList的findclass()的 Class c = pathList.findClass(name, suppressedExceptions); ....//部分代码 略 return c; }
可以发现这里是调用的
pathList
的findclass()
来加载的,那么我们再来一探pathList
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); //很清楚的看到 在`BaseDexClassLoader` 初始化的时候就构造了这么一个对象 this.pathList = new DexPathList(this, dexPath, libraryPathoptimizedDirectory); }
我们发现原来
pathList
就是DexPathList
的实例化对象,我们来瞄一眼这个类的构造函数,来稍微了解下这个对象是做什么的
public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) { ...//省略部分代码 //这里都是些初始化和验证的代码,我们主要关注的还是他初始化dexElements的方法 this.dexElements = makeDexElements(splitDexPath(dexPath),optimizedDirectory,suppressedExceptions); ...//省略部分代码 }
从上面的代码我们只是找到
DexPathList
的核心成员变量dexElements
现在我们来看看DexPathList#makeDexElements
中做了些什么
/** * Makes an array of dex/resource path elements, one per element of * the given array. */ private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,ArrayList<IOException> suppressedExceptions) { //这个就是我们最后返回的Element数组 我们重点的关注对象205 ArrayList<Element> elements = new ArrayList<Element>();206 /*207 * Open all files and load the (direct or contained) dex files208 * up front.加载所有文件209 */210 for (File file : files) {//遍历所有文件211 File zip = null;212 DexFile dex = null;213 String name = file.getName();214215 if (file.isDirectory()) {//如果是个文件夹 那么这里不是我们关系的216 // We support directories for looking up resources.217 // This is only useful for running libcore tests.218 elements.add(new Element(file, true, null, null));219 } else if (file.isFile()){220 if (name.endsWith(DEX_SUFFIX)) {221 // Raw dex file (not inside a zip/jar).222 try { //这里就是得到我们的dex文件,不再展开讲了,其实就是一 //个普通file的包装223 dex = loadDexFile(file, optimizedDirectory);224 } catch (IOException ex) {225 System.logE("Unable to load dex file: " + file, ex);226 }227 } else {228 zip = file;229230 try {231 dex = loadDexFile(file, optimizedDirectory);232 } catch (IOException suppressed) {233 /*234 * IOException might get thrown "legitimately" by * the DexFile constructor if235 * the zip file turns out to be resource-only *(that is, no classes.dex file236 * in it).237 * Let dex == null and hang on to the exception to * add to the tea-leaves for238 * when findClass returns null.239 */240 suppressedExceptions.add(suppressed);241 }242 }243 } else {244 System.logW("ClassLoader referenced unknown path: " + file);245 }246247 if ((zip != null) || (dex != null)) { //添加到Element数组,其实这里整块的操作就是将我们本地的dex文件进行加载248 elements.add(new Element(file, false, zip, dex));249 }250 }251252 return elements.toArray(new Element[elements.size()]);253 }
看到这里,我们基本上也了解了
DexPathList
是个什么东西了,他加载了我们本地的 dex 文件,并保存在了他的成员变量elements
数组中,ok,那么最后我们来看看他的DexPathList#findclass()
看看他是怎么加载一个类的
public Class findClass(String name, List<Throwable> suppressed) {334 for (Element element : dexElements) {//这里就是遍历加载的dex文件的集合的element数组335 DexFile dex = element.dexFile;336337 if (dex != null) { //这里就是真正加载一个类文件了,该方法会调用到底层的c库来进行类的加载 //在往底层就不在本文的讨论范围了338 Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);339 if (clazz != null) {340 return clazz;341 }342 }343 }344 if (dexElementsSuppressedExceptions != null) {345 suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));346 }347 return null;348 }
那么我们继续追下去到 DexFile#loadClassBinaryName()
会发现最后是一个native方法来加载一个这个类,再往底层就不在本文讨论范围之内了,到这里,我们的类才最终被我们加载出来了,本文也就差不多结束了,并没有什么很难懂的地方,本文也主要是为了更深入的理解Android和为当下比较热门的热修复和插件化打一点基础
- Android ClassLoader详解
- Android ClassLoader详解
- Android ClassLoader与JAVA ClassLoader详解及对比
- Android动态加载之ClassLoader详解
- Android中的dex、apk、ClassLoader详解
- Android动态加载之ClassLoader详解
- ClassLoader详解
- ClassLoader详解
- ClassLoader详解
- ClassLoader 详解
- 详解ClassLoader
- ClassLoader详解
- classloader详解
- CLASSLOADER详解
- Java ClassLoader详解
- Java ClassLoader详解[转载]
- Java ClassLoader详解
- Java ClassLoader详解
- 002.SSM之Spring MVC
- 百度地图JSAPI实现加载当前位置并导航到目的地(web应用)
- 微信、支付宝的支付系统,帮您管钱挣钱
- Rxjava 初始源码探究
- 深度学习笔记——算法总结
- Android ClassLoader详解
- gitlab 安装
- Boost电路的驱动电路
- 背景差分法示例
- 调通sina33m下的ap6181版本(分色排版)V1.0
- 深度学习——keras
- 微信小程序--搜索电影app(续)
- 安卓引用方法时的错误
- 2017 上半年总结