Android中ClassLoader源码解析之真的是你认为的ClassLoader

1.前言

首先,阅读本文章之前,需要了解java中的ClassLoader的基本原理,包括java中的三级ClassLoader机制以及ClassLoader的委托机制,否则下面的内容会不知道在讲什么。虽然Android中的ClassLoader也是遵循其委托机制,但是他没有遵循java的三级ClassLoader机制,而是自己造了一个,修改了java系统的代码,如果将两者混淆的话,在Android中使用ClassLoader的时候,你可能会遇到想不通的问题。(因为作者曾经就踩过坑。。。。。。)

 

首先,我们看下面一段代码,在Application的onCreate()中添加如下代码:

PathClassLoader classLoader = (PathClassLoader) getApplicationContext().getClassLoader();
Log.d("mytest", "classLoader : " + classLoader + "\n" +
            "parent : " + classLoader.getParent() + "\n" +
            "grandParent : " + classLoader.getParent().getParent() + "\n" +
            "system classloader : " + ClassLoader.getSystemClassLoader() + "\n" +
            "system parent : " + ClassLoader.getSystemClassLoader().getParent());

 

代码的执行结果,打印内容如下:

 classLoader : dalvik.system.PathClassLoader[dexPath=/data/app/com.gavin.demo2application-1.apk,libraryPath=/data/app-lib/com.gavin.demo2application-1]
 parent : java.lang.BootClassLoader@41099128
 grandParent : null
 system classloader : dalvik.system.PathClassLoader[dexPath=.,libraryPath=null]
 system parent : java.lang.BootClassLoader@41099128

 

看到上面的打印的内容,我们了解到在Android的项目中使用的ClassLoader是其自定义的PathClassLoader,最重要的一点是打印的dexPath,这个决定了在项目代码中要加载的类的位置(后面详细讲解)。

第二点奇怪的地方就是它的parent是 BootClassLoader,这个又是什么东西,后面详细讲解。

第三点调用ClassLoader.getSystemClassLoader()返回的是PathClassLoader,并且dexPath为. ,我们了解的java中的ClassLoader.getSystemClassLoader()返回的是加载classpath里面的class的ClassLoader,也就是java中的第三级ClassLoader,它是调用sun.misc.Launcher的getClassLoader()方法获取的,详细解析请自行查阅。

第四点它的parent也是BootClassLoader,看来这个必须要分析一下下,毕竟出镜率这么高。

2.Context.getClassLoader()返回的是PathClassLoader

首先,Android中可以使用的CLassLoader有PathClassLoader和DexClassLoader,PathClassLoader只能加载dex文件,我们安装apk之后会在/data/dalvik-cache目录下生产一个名为data@[email protected]@classes.dex的 ODEX 文件,而PathClassLoader要加载apk的时候会到这个文件夹下找对应的dex文件。(ODEX文件就是经过优化的dex文件,详细自行查阅),同时也是我们自己编写的项目中使用的ClassLoader。而DexClassLoader可以加载apk,dex,jar文件,就是被用来实现动态加载机制,加载一个外部的apk文件,实现完全解耦的模块式开发,现在的开源框架有DL(使用代理的方式,其实加载的不是插件中的类)和DroinPlugin(hook掉AMS和PMS实现);现在比较火的热修改也是和其有关系,比如AndFix(它是在运行时将java方法修改成native方法,然后修改调用这个方法的指针,指向修复的方法),nuwa(也就是qq空间实现基于dex分包,修改CLassLoader中的dexElements中的dex顺序实现)以及最新的美团的Robust(基于Android Studio的instance run的原理,为每个类创建代理类)。

关于上面所提的动态加载框架,热修复框架等等都会在后续的文章中进行分析。

 

上面说了那么多,现在开始分析PathClassLoader和DexCLassLoader的源码实现,他们都是继承BaseDexClassLoader,所有的实现都疯转在了这个类里面,先来看看PathClassLoader和DexClassLoader的源码.

PathClassLoader的代码:

public class PathClassLoader extends BaseDexClassLoader {

37    public PathClassLoader(String dexPath, ClassLoader parent) {
38        super(dexPath, null, null, parent);
39    }
40

63    public PathClassLoader(String dexPath, String libraryPath,
64            ClassLoader parent) {
65        super(dexPath, null, libraryPath, parent);
66    }
67}
68

 

DexClassLoader源码如下:

public class DexClassLoader extends BaseDexClassLoader {

55    public DexClassLoader(String dexPath, String optimizedDirectory,
56            String libraryPath, ClassLoader parent) {
57        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
58    }
59}

可以看出他们仅仅是重写了构造函数,所以所有的实现都是在BaseDexClassLoader里面。
参数:dexPath:要加载的apk或者jar文件的路径,optimizedDirectory:从apk中解析出dex文件存储的路径,libraryPath:apk文件中类要使用的c/c++代码,parent:父装载器,也就是真正loadclass的装载器。

 

下面重点来了,分析BaseDexClassLoader的源码,首先构造函数如下:

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }

super()走的是ClassLoader的设置parent的ClassLoader的方式,重点是下面的PathList,他存储的是dex的集合,因为apk是可以dex分包,它里面含有一个DexElement的集合,每一个Element就对应一个dex文件。

 

DexPathList的构造函数的核心代码如下:

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions);

makeDexElements()就是解析dex文件成对应的DexElement,代码如下:

private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                            ArrayList<IOException> suppressedExceptions) {
        ArrayList<Element> elements = new ArrayList<Element>();
209        /*
210         * Open all files and load the (direct or contained) dex files
211         * up front.
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值