本文转载自: http://blog.csdn.net/wy353208214/article/details/50859422

1.什么是类加载器?

类加载器(class loader)是 Java™中的一个很重要的概念。类加载器负责加载 Java 类的字节代码到 Java 虚拟机中。
Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。
基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例,需要了解ClassLoader可以参考这篇文章深入ClasssLoader

2.Dalvik虚拟机类加载机制

Dalvik虚拟机如同其他Java虚拟机一样,在运行程序时首先需要将对应的类加载到内存中。而在Java标准的虚拟机中,类加载可以从class文件中读取,也可以是其他形式的二进制流,因此,我们常常利用这一点,在程序运行时手动加载Class,从而达到代码动态加载执行的目的,但是Dalvik虚拟机毕竟不算是标准的Java虚拟机,因此在类加载机制上,它们有相同的地方,也有不同之处。
 
我们先看下下面这张关于Android Classload机制的图。
 
 

与JVM不同,Dalvik的虚拟机不能用ClassCload直接加载.dex,Android从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader;而这两个类就是我们加载dex文件的关键,这两者的区别是:

1.DexClassLoader:可以加载jar/apk/dex,可以从SD卡中加载未安装的apk;
2.PathClassLoader:要传入系统中apk的存放Path,所以只能加载已经安装的apk文件。

关于Android 动态加载基础 ClassLoader工作机制大家可以参考这里:https://segmentfault.com/a/1190000004062880

准备工作开始

一、打开Android studio 新建工程:
 
 
工程目录是这样的:
 
动态加载进来的class如何使用,一般有2种办法,一种是使用反射调用,这种我不多做介绍;还有一种是使用接口编程的方式来调用对应的方法,毕竟.dex文件也是我们自己维护的,所以可以把方法抽象成公共接口,把这些接口也复制到主项目里面去,就可以通过这些接口调用动态加载得到的实例的方法了。
接下来我们源码包下面新建一个包名称是dynamic,然后在dynamic下新建一个interface接口Dynamic,里面有个接口方法,就叫sayHello()吧,返回一个String,到时候我们可以通过Toast弹出来,Dynamic.java:
  1. package wangyang.zun.com.mydexdemo.dynamic;
  2. /**
  3. * Created by WangYang on 2016/3/11.
  4. */
  5. public interface Dynamic {
  6. String sayHello();
  7. }
接着我们新建一个impl包,并实现Dynamic接口,DynamicImpl.java:
  1. package wangyang.zun.com.mydexdemo.dynamic.impl;
  2. import wangyang.zun.com.mydexdemo.dynamic.Dynamic;
  3. /**
  4. * Created by WangYang on 2016/3/11.
  5. */
  6. public class DynamicImpl implements Dynamic {
  7. @Override
  8. public String sayHello() {
  9. return new StringBuilder(getClass().getName()).append(" is loaded by DexClassLoader").toString();
  10. }
  11. }

很简单输出一句话,"DynamicImpl is loaded by DexClassLoader."

具体的工程目录如下图:
 
点击Build -> make project,这时候会在build\intermediates\classes\debug目录下生成对应的classes文件。
 
 
好了我们要把DynamicImpl这个class转换成Dalvik可识别的dex文件,分两步:
1.先导出DynamicImpl这个类为jar包的形式;
2.通过android sdk自带的dx.jar工具转换jar包为dex文件。
完成第一步,当时遇到点麻烦,由于eclipse是基于ant并且有可视化工具,可以直接导出指定文件的jar包,但是android studio不行,那怎么办呢?
我们可以通过gradle task来打包,打开app目录下的build.gradle文件,切记不是根目录的build.gradle文件,加上以下代码:
  1. //删除dynamic.jar包任务
  2. task clearJar(type: Delete) {
  3. delete 'libs/dynamic.jar'
  4. }
  5. //打包任务
  6. task makeJar(type:org.gradle.api.tasks.bundling.Jar) {
  7. //指定生成的jar名
  8. baseName 'dynamic'
  9. //从哪里打包class文件
  10. from('build/intermediates/classes/debug/wangyang/zun/com/mydexdemo/dynamic/')
  11. //打包到jar后的目录结构
  12. into('wangyang/zun/com/mydexdemo/dynamic/')
  13. //去掉不需要打包的目录和文件
  14. exclude('test/', 'Dynamic.class', 'BuildConfig.class', 'R.class')
  15. //去掉R$开头的文件
  16. exclude{ it.name.startsWith('R$');}
  17. }
  18. makeJar.dependsOn(clearJar, build)

打开AS的 terminal窗口: cd app进入app目录,执行gradle makeJar,然后等待直到出现Build Successfully,这时会在build目录下出现libs/dynamic.jar文件,这个文件就是我们要用的jar包,我们可以使用jd-gui打开看下是不是只有DynamicImpl这个class;

第二步,使用sdk提供的dx.jar将导出的dynamic.jar转换成Dalvik可识别的dex格式,新版的sdk已经将dx.jar放到build-tools\23.0.2\lib目录下,我们在dos下或者在Android studio terminal下面进入到此目录,然后运行下面的命令:
dx --dex --output=dynamic_dex.jar dynamic.jar

 
output是你的输出目录,默认就是在当前的根目录下,执行完成后我们就在当前目录下生成了Davilk虚拟机可执行的dex文件,因为这条命令同时会打包dex文件,因此后缀是jar,我们用jd-gui打开dynamic.jar和dynamic_dex.jar这两个文件,看下他们有的结构。
 
 
 
 
可以看到,打包后的文件其实是一个classes.dex文件,目前为止我们要做的工作已经准备就绪了,接下来就是要在demo中使用这个dex文件。
 
二、删除刚刚新建的impl包以及包内的文件:
因为等下我们要使用的是dex下面的IDynamic实现类,所以我们需要删除当前工程下的IDynamic.java文件和impl包,避免运行时出错。同时,我们要把刚刚生成的dynamic_dex.jar文件放到assets目录下,等下需要把它copy到app/data下使用,删除后的整个工程目录如下:
 
 
FileUtils类是从assets目录下copy文件到app/data/cache目录,源码如下:
  1. public class FileUtils {
  2. public static void copyFiles(Context context, String fileName, File desFile) {
  3. InputStream in = null;
  4. OutputStream out = null;
  5. try {
  6. in = context.getApplicationContext().getAssets().open(fileName);
  7. out = new FileOutputStream(desFile.getAbsolutePath());
  8. byte[] bytes = new byte[1024];
  9. int i;
  10. while ((i = in.read(bytes)) != -1)
  11. out.write(bytes, 0 , i);
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }finally {
  15. try {
  16. if (in != null)
  17. in.close();
  18. if (out != null)
  19. out.close();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. public static boolean hasExternalStorage() {
  26. return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
  27. }
  28. /**
  29. * 获取缓存路径
  30. *
  31. * @param context
  32. * @return 返回缓存文件路径
  33. */
  34. public static File getCacheDir(Context context) {
  35. File cache;
  36. if (hasExternalStorage()) {
  37. cache = context.getExternalCacheDir();
  38. } else {
  39. cache = context.getCacheDir();
  40. }
  41. if (!cache.exists())
  42. cache.mkdirs();
  43. return cache;
  44. }
  45. }
打开MainActivity:
  1. public class MainActivity extends AppCompatActivity {
  2. private Dynamic dynamic;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. //添加一个点击事件
  8. findViewById(R.id.tx).setOnClickListener(new View.OnClickListener() {
  9. @Override
  10. public void onClick(View v) {
  11. loadDexClass();
  12. }
  13. });
  14. }
  15. /**
  16. * 加载dex文件中的class,并调用其中的sayHello方法
  17. */
  18. private void loadDexClass() {
  19. File cacheFile = FileUtils.getCacheDir(getApplicationContext());
  20. String internalPath = cacheFile.getAbsolutePath() + File.separator + "dynamic_dex.jar";
  21. File desFile = new File(internalPath);
  22. try {
  23. if (!desFile.exists()) {
  24. desFile.createNewFile();
  25. FileUtils.copyFiles(this, "dynamic_dex.jar", desFile);
  26. }
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. //下面开始加载dex class
  31. DexClassLoader dexClassLoader = new DexClassLoader(internalPath, cacheFile.getAbsolutePath(), null, getClassLoader());
  32. try {
  33. Class libClazz = dexClassLoader.loadClass("wangyang.zun.com.mydexdemo.dynamic.impl.IDynamic");
  34. dynamic = (Dynamic) libClazz.newInstance();
  35. if (dynamic != null)
  36. Toast.makeText(this, dynamic.sayHelloy(), Toast.LENGTH_LONG).show();
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. }
程序运行的效果图如下:
至此,我们关于Android Dex动态加载机制的原理讲到这里,接下来我会分析下通过Dex实现热修复的基本原理。
 
Demo源码地址:https://github.com/wy353208214/MyDexDemo

[转载] Android动态加载Dex机制解析的更多相关文章

  1. Android 插件技术:动态加载dex技术初探

    1.Android动态加载dex技术初探 http://blog.csdn.net/u013478336/article/details/50734108 Android使用Dalvik虚拟机加载可执 ...

  2. Android 动态加载 (二) 态加载机制 案例二

    探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法 重要说明 在实践的过程中大家都会发现资源引用的问题,这里重点声明两点: 1. 资源文件是不能直接inflate的,如果简单的话直接在程序 ...

  3. Android 动态加载 (一) 态加载机制 案例一

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

  4. Android动态加载jar/dex

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

  5. Android应用安全之外部动态加载DEX文件风险

    1. 外部动态加载DEX文件风险描述 Android 系统提供了一种类加载器DexClassLoader,其可以在运行时动态加载并解释执行包含在JAR或APK文件内的DEX文件.外部动态加载DEX文件 ...

  6. Android动态加载框架汇总

    几种动态加载的比较 1.Tinker 用途:热修复 GitHub地址:https://github.com/Tencent/tinker/ 使用:http://www.jianshu.com/p/f6 ...

  7. Android应用开发提高系列(4)——Android动态加载(上)——加载未安装APK中的类

    前言 近期做换肤功能,由于换肤程度较高,受限于平台本身,实现起来较复杂,暂时搁置了该功能,但也积累了一些经验,将分两篇文章来写这部分的内容,欢迎交流! 关键字:Android动态加载 声明 欢迎转载, ...

  8. 【Android】Android动态加载Jar、APK的实现

    本文介绍Android中动态加载Jar.APK的实现.而主要用到的就是DexClassLoader这个类.大家都知道Android和普通的Java虚拟机有差别,它只能加载经过处理的dex文件.而加载这 ...

  9. android动态加载

    转载自: http://www.cnblogs.com/over140/archive/2012/03/29/2423116.html http://www.cnblogs.com/over140/a ...

随机推荐

  1. ArcGIS Engine中数据的加载 (转)

    1.加载Shapefile数据 1 IWorkspaceFactory pWorkspaceFactory; 2 IFeatureWorkspace pFeatureWorkspace; 3 IFea ...

  2. Codeforces 730I [费用流]

    /* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给两行n个数,要求从第一行选取a个数,第二行选取b个数使得这些数加起来和最大. 限制条件是第一行选取了某个数的条件下,第二行不能选取对应位置的数. ...

  3. 从主机访问虚拟机上的Apache

    问题:VMWARE上安装的CentOS6.4,安装Apache,启动后,虚拟机上能访问,主机能ping通,但无法访问Apache. 原因:防火墙设置,配置iptables,开放apache的端口80

  4. ORM艰辛路之EF

    经过一段时间对EF的研究,发现EF还是有很大的作用的,起码比自己写代码快捷许多.不过往往一个学习一个新东西开始都是简单的,后面才慢慢了解到它的许多不方便 优点: EF在对一个实体的增删改以及继承方面做 ...

  5. 不安装Oracle客户端情况下使用PL/SQL 远程连接数据库

    附送PL/SQL Developer11中文版下载地址 1.先到Oracle网站下载Instant Client : http://www.oracle.com/technetwork/databas ...

  6. IOS 在不打开电话服务的时候,可以响应服务器的推送消息,从而接收服务器的推送消息

    在做即时通讯(基于xmpp框架)的时候遇到这样一个问题,就是在真机测试的时候,你按Home键返回桌面,在你返回桌面的时候,这是你的程序的挂起状态的,在你挂起的时候, 相当于你的程序是死的,程序的所有进 ...

  7. arcgis_engine_develop_error_42

    解决: 今天在VS2013打开程序时,手工添加了pageLayoutControl隔一会弹出错误窗口:Provide your license server administrator with th ...

  8. Sublime插件安装

    来在Sublime text3上安装Package Control 使用Ctrl+`(ESC下边的那个~)快捷键或者通过View->Show Console菜单打开命令行,粘贴如下代码: imp ...

  9. js 判断IOS版本号

    先来观察 iOS 的 User-Agent 串: Phone 4.3.2 系统:Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_2 like Mac OS X; e ...

  10. C学习笔记

    1.struct struct 是一种复合数据类型,其构成元素可以是一些复合数据类型,如array,struct,union,缺省情况下,编译器为结构体的每个成员按其自然对齐(默认对齐,按照结构体成员 ...