安卓的动态加载必备知识之一,DexClassLoader。
DexClassLoader当然也是一种ClassLoader,但本身属于顾名思义是用来加载Dex文件的,是安卓系统独有的一种类加载器。
基础概念
在此之前可以稍微回顾下ClassLoader的相关基础:
- ClassLoader是用来加载class文件的,它负责将*.class加载为内在中的Class对象
- 加载机制为“双亲委派”,即能交给父类加载器去加载的,绝不自行加载
再来看DexClassLoader:
官方说明了DexClassLoader的定义:
- 用于加载包含*.dex文件的jar包或apk文件
- 要求一个应用私有可写的目录去缓存编译的class文件
- 不允许加载外部存储空间的文件,以防注入攻击
那么很明显,我们普通项目中的jar包是不能直接用来加载的了。
在Java项目中,一般加载jar包会使用URLClassLoader,但安卓davlik不能使用,安卓只能使用继承自BaseDexClassLoader的两种ClassLoader—一种是PathClassLoader,用于加载系统中已经安装的apk;一种就是DexClassLoader,加载未安装的jar包或apk。
所以如果在安卓系统上想要动态加载jar包,DexClassLoader可能是唯一的选择。
DexClassLoader源码
使用方法
使用很简单,只需要清楚其构造方法的参数意义就可以。
DexClassLoader (String dexPath,
String optimizedDirectory,
String librarySearchPath,
ClassLoader parent)
参数 | 含义 |
---|---|
dexPath | 包含dex文件的jar包或apk文件路径 |
optimizedDirectory | 释放目录,可以理解为缓存目录,必须为应用私有目录,不能为空 |
librarySearchPath | native库的路径,可为空 |
parent | 父类加载器 |
那么使用起来就是这样:
private void loadJar1(){
String file = Environment.getExternalStorageDirectory() + "/jinxingateway/jar/codec.jar";
String path = getDir("jar", MODE_PRIVATE).getAbsolutePath() + File.separator + "codec.jar";
try {
//复制到私有目录
FileOperator.copy(file, path);
DexClassLoader dcl = new DexClassLoader(path, getDir("jar", MODE_PRIVATE).getAbsolutePath(), null, getClassLoader());
Class<?> factoryCls = dcl.loadClass("com.jinxin.codec.ProtocolCodecFactory");
Class<?> extensionCls = dcl.loadClass("com.jinxin.codec.ICodecExtension");
//省略其下利用反射的代码
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
因为读写权限的敏感,所以jar包一般也会复制到应用私有路径中再行加载,加载后使用loadClass方法即可得到类,然后利用反射就可以得到类中的各种信息了。
这里的加载是不能使用Class.forName的,因为这样使用最终调用的就是java的ClassLoader而不是DexClassLoader了:
public static Class<?> forName(String className)
throws ClassNotFoundException {
return forName(className, true, VMStack.getCallingClassLoader());
}
扩展
DexClassLoader只能加载dex,那普通的jar包怎么办?
当然是转成dex了。
使用安卓sdk/build-tools目录下的dx工具,即可把普通的jar包转换为包含dex的jar包。在能运行dx.bat的情况下使用命令:
dx --dex --output [输出文件] [输入文件]