Android动态加载器

Android动态加载器

前言

每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。识别的是dex文件,而不是class文件。供类加载的文件也只能是dex文件,或者包含有dex文件的.apk或.jar文件类。

类加载方式

1、由 new 关键字创建一个类的实例
如:Student student= new Student();

2、调用 Class.forName() 方法
通过反射加载类型,并创建对象实例
如:Class clazz = Class.forName("Student ");
Object dog =clazz.newInstance();

3、调用某个 ClassLoader 实例的 loadClass() 方法
通过该 ClassLoader 实例的 loadClass() 方法载入。应用程序可以通过继承 ClassLoader 实现自己的类装载器。
如:Class clazz = classLoader.loadClass("Student ");
Object dog =clazz.newInstance();

三者的区别:
(1)1和2使用的类加载器是相同的,都是当前类加载器。(即:this.getClass.getClassLoader)。3由用户指定类加载器。
(2)1是静态加载,2、3是动态加载。

方式1、2都是很简单,这里来学习下第3种方案。

动态加载方式

先来了解下dex文件:
一.dex文件
Dex全称是Dalvik VM executes,即Android Dalvik虚拟机的可执行文件,它不是JavaME的字节码而是Dalvik字节码,包含应用程序的全部操作指令以及运行时数据。它的加载是通过ClassLoader来完成,包括很多子类。

二.动态加载器
dex的加载是通过ClassLoader来完成,包括很多子类。如下:
BootClassLoader(Java的BootStrap ClassLoader) :用于加载Android Framework层class文件。
PathClassLoader(Java的App ClassLoader) :用于加载已经安装到系统中的apk中的class文件。
DexClassLoader(Java的Custom ClassLoader) :用于加载指定目录中的class文件。
对应App应用开发,先来了解下DexClassLoader和PathClassLoader:

1、DexClassLoader,进行类加载,文件类型可以是jar、apk、dex(因为Dalvik虚拟机识别的是dex文件,而不是class文件。供类加载的文件也只能是dex文件,或者包含有dex文件的.apk或.jar文件),且可以从任意路径加载文件。类关系如下:

public class BaseDexClassLoader extends ClassLoader {
    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
        throw new RuntimeException("Stub!");
    }
}
public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}
}

由上面代码可见,一个DexClassLoader的构造参数可以指定dex路径,优化后的dex路径,和动态库的路径。所以能加载任意目录下的文件。
(1)参数理解:
dexPath:要加载的dex文件路径。
optimizedDirectory:dex文件要被copy到的目录路径。
libraryPath:apk文件中类要使用的c/c++代码。
parent:父装载器,也就是真正loadclass的装载器。

如果对这几个参数String dexPath, File optimizedDirectory, String libraryPath不了解,做以下实验:
在任意类中增加打印日志:
Log.d(“TAG”, “getClassLoader:” + this.getClass().getClassLoader());
输出如下:
dalvik.system.PathClassLoader[DexPathList[[zip file “/system/framework/android.test.runner.jar”, zip file “/data/app/com.starcor.hunan-1.apk”],nativeLibraryDirectories=[/data/app-lib/com.starcor.hunan-1, /vendor/lib, /system/lib]]]
(2)使用DexClassLoader:
File path = new File(“xxxx”);
File parentFile = path.getParentFile(); //指定路径:用来释放.apk包或者.jar包中的dex文件
ClassLoader _classLoader = new DexClassLoader(file.getAbsolutePath(), parentFile.getAbsolutePath(), null, getClass().getClassLoader());

2、PathClassLoader,不能主动从zip包中释放出dex,只支持直接操作dex格式文件,或者已经安装的apk(已安装的apk在cache中存在缓存的dex文件:/data/dalvik-cache目录下),其它位置的文件加载的时候都会出现 ClassNotFoundException。

public class BaseDexClassLoader extends ClassLoader {
    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
           throw new RuntimeException("Stub!");
    }
}
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
    //此处传递的optimizedDirectory、libraryPath为null
        super((String)null, (File)null, (String)null, (ClassLoader)null);
    }
}

可以看到,DexClassLoader的构造方法的区别就是少了optimizedDirectory(把dex文件copy到的目录路径)。正是因为缺少这个路径,PathClassLoader只能用来加载安装过的apk中的dex文件(可以去查看源码)。

/data/dalvik-cache目录如下图所示:
在这里插入图片描述

3、两种类加载总结:
(1)相同点:
独立的类名称空间+类加载隔离:
每个加载器,有自己的独立的类名称空间。JVM 及 Dalvik 对类唯一的识别是 ClassLoader id + PackageName + ClassName,即ClassLoader如果不同,两个类必定不等(基于反射功能,可以实现不同版本的class类的测试即同一个类的不同版本的共存)。另外,对于同一个类加载器实例来说,名字相同的类只能存在一个,并且仅加载一次。

类加载共享
同一个类名空间中,类加载会先看这个类是不是已经被 loaded 过,没有的话则去他的 parent 去找,如此递归,称之为双亲委托。当一个class文件被任何一个ClassLoader加载过,就不会再被其他ClassLoader加载。

(2)不同点:
由上面的代码对比,可以知道Android系统默认的类加载器为PathClassLoader,而DexClassLoader可以加载reny任意路径下的jar、apk、dex。

4、优点
好处是:可以使类动态的加载到JVM中并运行,即可以在程序运行时候再加载类,提供了很灵活的动态加载方式。

基于以上的特点,我们可以使用类加载器实现模块的隔离。隔离后,各个模块是独立的,不能进行数据访问,独立模块存在于程序中运行。这样带来的好处,就是当App应用庞大起来,我们可使用该方式进行插件化开发,多个团队独立开发,并且能做到插件更新,不需要升级APP。后续会来讲解插件开发。

参考资料:
https://www.jianshu.com/p/4b4f1fa6633c
https://blog.csdn.net/colinandroid/article/details/80712045
https://www.cnblogs.com/liushijie/p/4893535.html
https://www.ibm.com/developerworks/cn/java/j-lo-hotswapcls/index.html
https://blog.csdn.net/szwandcj/article/details/51473265
http://www.trinea.cn/android/java-loader-common-class/

源码:
http://androidxref.com/7.1.2_r36/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
http://androidxref.com/7.1.2_r36/xref/libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
http://androidxref.com/7.1.2_r36/xref/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值