Android的ClassLoader体系

在Android中可以跟java一样实现动态加载jar,但是Android使用Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik byte code
 Android在API中给出可动态加载的有:DexClassLoader 和 PathClassLoader。

DexClassLoader:可加载jar、apk和dex,可以从SD卡中加载
PathClassLoader:只能加载已安装到系统中(即/data/app目录下)的apk文件

为什么PathClassLoader只能加载apk的文件?

从上图可以明显知道他们都继承BaseDexClassLoader 在看看他们源码有什么区别

// DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
} // PathClassLoader.java
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);
}
}

主要的区别在于PathClassLoader的optimizedDirectory参数只能是null,那么optimizedDirectory是做什么用的呢?需要去父类看BaseDexClassLoader

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

他们的父类构造方法中 是new 一个DexPathList实例把optimizedDirectory参数传入

 public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
……
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
} private static Element[] makeDexElements(ArrayList<File> files,
File optimizedDirectory) {
ArrayList<Element> elements = new ArrayList<Element>();
for (File file : files) {
ZipFile zip = null;
DexFile dex = null;
String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) {
dex = loadDexFile(file, optimizedDirectory);
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
zip = new ZipFile(file);
}
……
if ((zip != null) || (dex != null)) {
elements.add(new Element(file, zip, dex));
}
}
return elements.toArray(new Element[elements.size()]);
} private static DexFile loadDexFile(File file, File optimizedDirectory)
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file);
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0);
}
} /**
* Converts a dex/jar file path and an output directory to an
* output file path for an associated optimized dex file.
*/
private static String optimizedPathFor(File path,
File optimizedDirectory) {
String fileName = path.getName();
if (!fileName.endsWith(DEX_SUFFIX)) {
int lastDot = fileName.lastIndexOf(".");
if (lastDot < 0) {
fileName += DEX_SUFFIX;
} else {
StringBuilder sb = new StringBuilder(lastDot + 4);
sb.append(fileName, 0, lastDot);
sb.append(DEX_SUFFIX);
fileName = sb.toString();
}
}
File result = new File(optimizedDirectory, fileName);
return result.getPath();
}

optimizedDirectory是用来缓存我们需要加载的dex文件的,并创建一个DexFile对象,如果它为null,那么会直接使用dex文件原有的路径来创建DexFile 对象。

optimizedDirectory必须是一个内部存储路径,无论哪种动态加载,加载的可执行文件一定要存放在内部存储。DexClassLoader可以指定自己的optimizedDirectory,所以它可以加载外部的dex,因为这个dex会被复制到内部路径的optimizedDirectory;而PathClassLoader没有optimizedDirectory,所以它只能加载存在系统中已经安装过的apk里面的内部的dex

怎么缓存经过优化的classes(odex文件)?

使用Context.getDir(String, int)方法可以创建一个这样的目录,例如:

File dexOutputDir = context.getDir("dex", 0);

DexClassLoader

public DexClassLoader (String dexPath, String dexOutputDir, String libPath, ClassLoader parent)

dexPath:dex文件路径列表,多个路径使用”:”分隔 
dexOutputDir:经过优化的dex文件(odex)文件输出目录 
libPath:动态库路径(将被添加到app动态库搜索路径列表中) 
parent:这是一个ClassLoader,这个参数的主要作用是保留java中ClassLoader的委托机制(优先父类加载器加载classes,由上而下的加载机制,防止重复加载类字节码)

PathClassLoader

PathClassLoader提供两个常用构造方法

public PathClassLoader (String path, ClassLoader parent)

public PathClassLoader (String path, String libPath, ClassLoader parent)

path:文件或者目录的列表 
libPath:包含lib库的目录列表 
parent:父类加载器

PathClassLoader提供一个简单的ClassLoader实现,可以操作在本地文件系统的文件列表或目录中的classes,但不可以从网络中加载classes。

DexClassloader

编写接口:Dynamic

package com.smilegames.dynamic.interfaces;

public interface Dynamic {
public String helloWorld(); public String smileGames(); public String fyt();
}

编写实现类:DynamicTest

package com.smilegames.dynamic.impl;

import com.smilegames.dynamic.interfaces.Dynamic;

public class DynamicTest implements Dynamic {

    @Override
public String helloWorld() {
return "Hello Word!";
} @Override
public String smileGames() {
return "Smile Games";
} @Override
public String fyt() {
return "fengyoutian";
} }

打包并编译成dex
       将接口打包成jar:dynamic.jar(只打包这Dynamic.java这一个接口)
       将实现类打包成jar:dynamic_test.jar(只打包DynamicTest.java这一个实现类)
       将打包后的实现类(dynamic_test.jar)编译成dex:dynamic_impl.jar
              1、将dynamic_test.jar拷贝到SDK安装目录android-sdk-windows\platform-tools下(ps:如果platform-tools没有dx.bat,可拷贝到build-tools目录下有dx.bat的子目录)
              2、执行以下命令:

 dx --dex --output=dynamic_impl.jar dynamic_test.jar

3、将dynamic.jar引入测试实例
             4、将dynamic_impl.jar放到模拟器或真机的sdcard

修改onCreate例子

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 或许activity按钮
helloWorld = (Button) findViewById(R.id.helloWorld);
smileGames = (Button) findViewById(R.id.smileGames);
fyt = (Button) findViewById(R.id.fyt); /*使用DexCkassLoader方式加载类*/
// dex压缩文件的路径(可以是apk,jar,zip格式)
String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "dynamic_impl.jar"; // dex解压释放后的目录
//String dexOutputDirs = Environment.getExternalStorageDirectory().toString();
File dexOutputDir = context.getDir("dex", 0);
DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDir.getAbsolutePath(),null,getClassLoader());
//主意这里目录必须是应用内的目录
//如果是应用外的目录例如String dexOutputDirs = Environment.getExternalStorageDirectory().toString()+"XXX";
//会报:java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks
//原因之前都已经讲过 // 定义DexClassLoader
// 第一个参数:是dex压缩文件的路径
// 第二个参数:是dex解压缩后存放的目录
// 第三个参数:是C/C++依赖的本地库文件目录,可以为null
// 第四个参数:是上一级的类加载器
DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDirs,null,getClassLoader()); Class libProvierClazz = null;
// 使用DexClassLoader加载类
try {
libProvierClazz = dexClassLoader.loadClass("com.smilegames.dynamic.impl.DynamicTest");
// 创建dynamic实例
dynamic = (Dynamic) libProvierClazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
} helloWorld.setOnClickListener(new HelloWorldOnClickListener());
smileGames.setOnClickListener(new SmileGamesOnClickListener());
fyt.setOnClickListener(new FytOnClickListener());
} private final class HelloWorldOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (null != dynamic) {
Toast.makeText(getApplicationContext(), dynamic.helloWorld(), 1500).show();
} else {
Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();
}
}
} private final class SmileGamesOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (null != dynamic) {
Toast.makeText(getApplicationContext(), dynamic.smileGames(), 1500).show();
} else {
Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();
}
}
}

DexClassLoader和PathClassLoader的更多相关文章

  1. DexClassLoader和PathClassLoader载入Dex流程

    0x00 在上一篇文章apk安装和优化原理,在最后我们分析了DexClassLoader和PathClassLoader的构造函数的不同. PathClassLoader最后调用的是new DexFi ...

  2. DexClassLoader和PathClassLoader类载入机制

    0x00 在DexClassLoader和PathClassLoader载入Dex流程一文中,我们分析了dex文件怎样形成了DexFile结构体.本文中解说类载入机制,实际上就是生成ClassObje ...

  3. Android热修复AndFix

    热修复主要用来修复代码.修复bug.添加独立的功能,他的原理主要是操作PathClassLoader.DexClassLoader. PathClassLoader是类加载器,DexClassLoad ...

  4. [转载] Android动态加载Dex机制解析

    本文转载自: http://blog.csdn.net/wy353208214/article/details/50859422 1.什么是类加载器? 类加载器(class loader)是 Java ...

  5. dex分包变形记

    腾讯Bugly特约作者:李金涛 一.背景 就在项目灰度测试前不久,爆出了在 Android 3.0以下手机上安装时出现 INSTALL _ FAILED_DEXOPT,导致安装失败.这一问题意味着项目 ...

  6. Android中插件开发篇之----动态加载Activity(免安装运行程序)

    一.前言 又到周末了,时间过的很快,今天我们来看一下Android中插件开发篇的最后一篇文章的内容:动态加载Activity(免安装运行程序),在上一篇文章中说道了,如何动态加载资源(应用换肤原理解析 ...

  7. Android中插件开发篇之----类加载器

    关于插件,已经在各大平台上出现过很多,eclipse插件.chrome插件.3dmax插件,所有这些插件大概都为了在一个主程序中实现比较通用的功能,把业务相关或者让可以让用户自定义扩展的功能不附加在主 ...

  8. Android中的动态加载机制

    在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本 ...

  9. 【转】Android类动态加载技术

    http://www.blogjava.net/zh-weir/archive/2011/10/29/362294.html Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的 ...

随机推荐

  1. 转/keystore和truststore的区别

    keytool是java自带的工具用于产生密钥 keystore可以看成一个放key的库,key就是公钥,私钥,数字签名等组成的一个信息. truststore是放信任的证书的一个store. 那他们 ...

  2. 数组队列C++实现

    template <typename _T>class CArrayQueue {public:    CArrayQueue() {        m_rear = 0;        ...

  3. webbrowser内容滚动(javascript内容无缝滚动)

    一 使用webbrowser现有方法 引用:https://blog.csdn.net/xiaokailele/article/details/48392673 public partial clas ...

  4. 几款Web服务器性能压力测试工具

    一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载. 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般 ...

  5. 不准使用xib自定义控制器view的大小

    1.AppDelegate.m // // 文 件 名:AppDelegate.m // // 版权所有:Copyright © 2018年 leLight. All rights reserved. ...

  6. Hawk-and-Chicken 强连通

    题意:一群人投票  票具有传递性  求出累计和最大的数和 哪几个人最大 强连通好题!!! 毫无疑问先强连通缩点 一开始打算拓扑排序求dis  但是发现拓扑排序会有重复累加的情况 那么就反向建图   当 ...

  7. 为什么选择Angular 2?

    没有选择是痛苦的,有太多的选择却更加痛苦.而后者正是目前前端领域的真实写照.新的框架层出不穷:它难吗?它写得快吗?可维护性怎样?运行性能如何?社区如何?前景怎样?好就业吗?好招人吗?组建团队容易吗? ...

  8. HashMap vs Hashtable

    一.散列 1. HashMap 1)  hashmap的数据结构 Hashmap是一个数组和链表的结合体(在数据结构称“链表散列“),如下图示: 当我们往hashmap中put元素的时候,先根据key ...

  9. Java基础之身份证验证

    //简约版package test; import java.util.Scanner; public class ID { /** * 匹配算法 : 1) 得到17位身份证号码与下面给出的17位 2 ...

  10. sql开发技巧总结-1

    1.数据库分类 关系型 非关系型 2.sql语句分类 sql: ddl数据库定义语言  tpl事物处理语言 dcl数据控制语言  dml数据操作语言(insert delete update sele ...