本文转载自: 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. makefile 函数集

    1 if 函数 语法 $(if CONDITION,THEN-PART[,ELSE-PART]) 功能 第一个参数"CONDITION",在函数执行时忽略其前导和结尾空字符,如果包 ...

  2. iOS收到Push后播放声音和震动

    一.APNS 1.注册 [cpp] [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNoti ...

  3. XF 文档 - Element Framework Doc

    配置篇 Chapter Configuration Schema篇 Chapter Schema 查询篇 Chapter Querying Data 数据更改及验证篇 Chapter Data Mod ...

  4. Business Unit Lookup in Form

    Just add the below code in lookup() of StringEdit control in Form to get the Business Unit Lookup: p ...

  5. js访问xml

    从w3school中获取代码 <html> <head> <script type="text/javascript"> var xmlhttp ...

  6. symbol(s) not found for architecture x86_64 之 linker command failed with exit code 1 (use -v to see invocation)解决方案排查

    这样的错误 ,我的解决方案是, 第一种:   查看他说在 ****.o 中,你要查看这样的关键点,然后去查看,你 项目中有没有引进这样的文件,在项目中查找,看项目中有没有,如果没有那就是没添加进来,你 ...

  7. appium + maven +jenkins 基本入门之二 新建maven 的java项目

    1: 下载maven : 1.0 :设置maven的环境变量: 1.1: 设置maven本地仓库: 在下载好的maven文件夹找到 apache-maven-3.3.9/conf 文件夹下的setti ...

  8. 图片链接hover移动的解决方案分析

    现实在网站中,我们经常看到有鼠标滑过某个图片,出现边框的效果.如下图:

  9. icePDF去水印方法

    原文:http://www.cnblogs.com/pcheng/p/5711660.html 1.首先下载到icepdf的架包. 2.用解压缩软件,对该jar文件进行解压,得到名为icepdf-co ...

  10. python基础之迭代与解析

    先简单看一下文件迭代器 >>> f=open('file1') >>> f.readline() "'aaa','bbb','ccc'\n" & ...