Android ClassLoader

本文参考《Android进阶解密》作者刘望舒,吃水不忘挖井人。

按照惯例先给大家来个段子。
哈哈哈嗝

正文开始

目录
1,javaClassLoader
2,AndroidClassLoader
3,知识点总结
image.png
1,Java中的ClassLoader
简介
类名 编写语言 加载目录
bootstrapCLassloader cpp JAVA_Home\jre\lib
ExtensionsClassloader java java_home\jre\lib\ext
applicationClassLoader java classpach 路径下

双亲委派机制

双亲委派机制的优点
1,避免重复加载相同的类,也就是一个类,无论从自己到父类只能有一个Loader进行加载,并且缓存
2,保证核心类的安全性,例如,自定义String想要去替换系统核心的类。
代码
  protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException{
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);//步骤1
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);//步骤2
                    } else {
                        c = findBootstrapClassOrNull(name);//步骤3
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
              //步骤4
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    c = findClass(name);
                }
            }
            return c;// 步骤5
    }
理论

当需要加载一个类的时候
1,首先判断是否加载过,如果有直接返回。
2,如果没有加载过,先让自己的父类去加载,父类再调用自己的父类加载,一直到父类为null的时候。
3,调用native方法进行加载也就是bootStrapClassloader进行加载。
4,如果父类能加载直接返回,如果没有加载到就自己去加载
5,返回加载的类。

2,Android中的ClassLoader

Android中的classLoader 和java 有所不同,我们都知道Java加载的是jar包文件,Android 加载的是dex文件

CLassLoader
名称 继承 作用
BootClassLoader classLoader Android中所有ClassLoader的parent
BaseDexClassLoader classLoader 加载dex文件,主要逻辑都在这里边
SecureClassLoader classLoader 增加安全性
URLClassLoader SecureClassLoader URL路径从jar文件中加载类和资源
PathClassLoader BaseDexClassLoader Android默认加载器,只能加载已经安装的apk文件/data/app目录)
DexClassLoader BaseDexClassLoader \color{red}{实现热修复的重点,加载任意目录下的dex文件。 }
InMemoryDexClassLoader BaseDexClassLoader 加载内存中的类文件

BaseDexClassLoader

构造函数

 public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                              String librarySearchPath, ClassLoader parent, boolean isTrusted) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);//1
        ...
    }

dexPath:包含类和资源的 apk文件列表,由 File.pathSeparator分隔,在Android上默认为:,(默认为冒号)
optimizedDirectory:由于dex文件被包含在APK,因此在装载目标类之前需要先从APK文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径。这也是对apk中dex根据平台进行ODEX优化的过程。自API26开始无效。
librarySearchPath:指目标类中所使用的C/C++库存放的路径,可以为null。
parent:父ClassLoader引用。

看一下BaseDexClassLoader 的findClass方法
 protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();

//这里是重点
        Class c = pathList.findClass(name, suppressedExceptions);//1
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;//2
        }
        return c;
    }

注意这里的pathList,在构造函数中创建,这是一个对象并不是一个集合,1处实际调用了pathList对象的findClass方法,第一个参数是全类名,第二个参数是异常集合,把方法内部抛出的异常,添加进去,这种类似C++的指针传了进去,在2处的代码抛出。

DexPathList 源码
 final class DexPathList {


    private final Element[] dexElements;//数组,这里其实就是把dex文件封装起来了
    
    public Class findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;

            if (dex != null) {
//核心 调用DexFile 元素的loadClassBinaryName方法去加载
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }
}

DexPathList 的findClass 方法会遍历当前数组,每一个对象Element拿到它封装的DexFile 对象调用 loadClassBinaryName去加载类。

Element 源码
 /*package*/ static class Element {
       private final File dir;
        private final boolean isDirectory;
        private final File zip;
        private final DexFile dexFile;
        private ZipFile zipFile;
        private boolean initialized;
DexFile 源码
public final class DexFile {

  //方法1
    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, suppressed);
    }
//方法2
  private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     List<Throwable> suppressed) {
        Class result = null;
        try {
//核心,最后调用native去进行加载
            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;
    }

}

知识点总结

1,双亲委派机制原理
2,java 和Android classLoader的不同
3,Android 加载类的流程
BaseDexClassLoader 持有一个pathList 对象,pathList对象内部持有一个Element对象数组,每个Element对象封装了一个DexFile对象
DexFile对象调用loadClassBinaryName 方法,最后调用native去进行加载。
为什么要介绍BaseDexClassLoader ,因为Android 加载dex文件的两个classloader PathClassLoader DexClassLoader都是他的子类,而且没有什么拓展只是控制了传入参数的不同。

PathClassLoader
public class PathClassLoader extends BaseDexClassLoader {
  public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

 public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
}

所有的构造函数,调用super的时候第二个参数都传入了null,这个参数是dex解压目录,所以我们不能操作PathClassLoader 加载的目录,这是Android 默认加载的类。

DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
    /**
     * Creates a {@code DexClassLoader} that finds interpreted and native
     * code.  Interpreted classes are found in a set of DEX files contained
     * in Jar or APK files.
     *
     * <p>The path lists are separated using the character specified by the
     * {@code path.separator} system property, which defaults to {@code :}.
     *
     * @param dexPath the list of jar/apk files containing classes and
     *     resources, delimited by {@code File.pathSeparator}, which
     *     defaults to {@code ":"} on Android
     * @param optimizedDirectory directory where optimized dex files
     *     should be written; must not be {@code null}
     * @param libraryPath the list of directories containing native
     *     libraries, delimited by {@code File.pathSeparator}; may be
     *     {@code null}
     * @param parent the parent class loader
     */
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

构造函数,第二个参数我们可以自己定义,所以我们可以操作DexClassLoader 加载的目录,这就是其中一种热修复的方案核心所在。

最后附上源码地址,Android源码

\color{#ff00ff}{哲学 。}

\color{#ff00ff}{许多赛跑的失败,都是失败在最后的几步。跑应跑的路已经不容易,“跑到尽头”当然更困难 —— 苏格拉底。}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容