Android 动态加载之DexClassLoader

Android提供动态加载机制,允许从SD卡中加载dex格式的文件,其中,DexClassLoader类起了关键作用。
首先看下Android Developer关于DexClassLoader的介绍,
    A class loader that loads classes from  .jar and  .apk files containing a  classes.dex entry. This can be used to execute code not installed as part of an application.
This class loader requires an application-private, writable directory to cache optimized classes. Use  Context.getCodeCacheDir() to create such a directory:
File dexOutputDir = context.getCodeCacheDir();
Do not cache optimized classes on external storage.  External storage does not provide access controls necessary to protect your application from code injection attacks.
简单翻译一下,DexClassLoader是一个加载.jar或.apk文件的类加载器,其中该文件是dex格式。其可以用来执行程序之外的其他代码。该加载器需要一个程序私有的文件,Google推荐使用context.getCodeChaceDir()目录来使用。
    我们来看下其构造函数,    
public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
Added in  API level 3

Creates a DexClassLoader that finds interpreted and native code. Interpreted classes are found in a set of DEX files contained in Jar or APK files.

The path lists are separated using the character specified by the path.separator system property, which defaults to :.

Parameters
dexPath the list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android
optimizedDirectory directory where optimized dex files should be written; must not be null
libraryPath the list of directories containing native libraries, delimited by File.pathSeparator; may be null
parent the parent class loader

其中,dexPath指定了要加载的文件目录; optimizedDirectory 是优化文件存放的路径,也就是上面提到的应该是程序私有的路径(其他程序无法访问的),libraryPath包含的native库路径,parent,指定父加载器。

该类除了构造器之外,没有其他方法,我们看下其继承关系。
java.lang.Object
    java.lang.ClassLoader
     ↳ dalvik.system.BaseDexClassLoader
       ↳ dalvik.system.DexClassLoader
看到,其继承了BaseDexClassLoader类, BaseDexClassLoader继承了ClassLoaer类,我们找到其源码,进行分析,
1. DexClassLoader类只有一个构造函数,将4个构造参数传给 BaseDexClassLoader,接下来分析 BaseDexClassLoader,
2. BaseDexClassLoader有两个成员变量,String originalPath和DexPathList pathList;看其注释, originalPath指的是其文件路径,pathlist是路径元素列表(原文是,structured lists of path elements,下文会分析), BaseDexClassLoader 重载了ClassLoader父类的一些方法,我们注重分析findclass,其代码如下,
      
      
      
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = pathList.findClass(name);
 
if (clazz == null) {
throw new ClassNotFoundException(name);
}
 
return clazz;
}
然而其并没有重载父类的loadClass方法(加载类时最主要用到的方法),我们接着分析ClassLoader类
3. ClassLoader
    其包含了很多方法,我们注重分析loadClass方法,
     
     
     
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
 
if (clazz == null) {
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
}
 
if (clazz == null) {
clazz = findClass(className);
}
}
 
return clazz;
}
    我们可以看到,当loadClass时,会先判断是否已加载过,若加载过,则不需要重新加载,若没有加载过,则调用parent.loadClass方法,若其返回为null(说明parent未加载成功),则调用findClass自己加载,上面的机制称为双亲委派机制。+
    好,接下来我们测试一下DexClassLoader能否成功加载外部代码。

1. 生成外部加载代码
    我们新建一个Android工程,其目录如下,

其中,ILib定义了一个接口,关于定义接口的好处,下面会提到。
      
      
      
public interface ILib {
public void sayHello();
}
Lib实现了ILIb接口,在sayHello中输出一句话
      
      
      
public class Lib implements ILib {
 
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("Hello from Thrid Dex Lib");
}
 
}
然后,让我们导出Lib类,选中Lib.java,右键export,选择java-jar file导出,命名为lib.jar,注意导出时,不要选择接口文件(ILib),否则会出错。
上面提到DexClassLoader加载的是Dex格式的文件,但是Eclipse导出的是jar格式,所以,我们得用Android的dx命令来将lib.jar编译为dex格式。dx命令位于Android sdk/build-tools/版本号/目录,为了方便可以将其路径加入到系统变量中,然后在cmd中切换到lib.jar所在目录,执行一下命令
dx --dex --output=mylib.jar lib.jar
得到mylib.jar文件

2. 动态加载
    得到test.jar文件后,我们将其放入到工程的aseet目录,由于DexClassLoader是从SD卡中加载代码的,所以我们在程序运行的时候,将test.jar从asset目录copy到SD卡中,注意copy到的路径必须是该程序私有的。
      
      
      
private void copy2SDCard(File dexInternalStoragePath) {
BufferedInputStream bis;
try {
bis = new BufferedInputStream(getAssets().open(LIB_JAR));
OutputStream dexWriter = new BufferedOutputStream(
new FileOutputStream(dexInternalStoragePath));
byte[]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值