android 加载外部 dex文件中的类 的源码实例

在一篇文章中,我们讨论了   安卓类加载机制分析  的理论知识, 这篇文章用一篇具体的实例来演示 插件化功能的 雏形。

类加载机制  是 研究插件化 和 热修复 的基础。 加载外部 dex文件中的类,我们在这部分主要做的流程有:

  • 1.编写基本的Java文件并编译为.class文件。
  • 2.将.class文件转为.dex文件。
  • 3.将转好的dex文件放入创建好的Android工程内并在启动时将其写入本地。
  • 4.加载解压后的.dex文件中的类,并调用其方法进行测试。

1、创建一个外部独立的java类文件

public class Test {

    public void print() {
        System.out.println("this is Test Class");
    }


    public String accessDex() {
        return "这是一段存储在独立dex文件中的字符串";
    }

}

2、用 javac 命令 生成  class 文件

android 加载外部 dex文件中的类 的源码实例_第1张图片

 

3、用 Android sdk/build-tools/ 中的 dx 工具 ,将 class 文件生成  dex  文件。 注意 build-tools 所有版本中都带有 dx工具,这里我用的是目前最新版本的 build-tools,即 27.0.3 版本.   

  dx 命令的基本用法

dx --dex [--output=] [.class | .{zip,jar,apk} | ]

我们使用的命令如下

dx --dex --output=1.dex Test.class

    mac 系统需要注意的是 dx的命令的执行路径。  在 Test.class 文件夹中 打开 终端, 将 sdk/build-tools/27.0.3/dx 文件 拖动到  终端中,表示要执行这个 可执行文件, 它接收三个参数,   

参数1:--dex 表示要执行打包 dex动作; 
参数2: --output=1.dex 表示设置生成的文件的名称;   
参数3:需要转换的class文件

android 加载外部 dex文件中的类 的源码实例_第2张图片
同理,还可以使用此命令,将一个jar包转换成 dex文件,其中 origin.jar是已经存在的、需要被转换的jar文件,  target.dex 是转换后生成的 dex 文件名称

dx --dex --output=target.dex origin.jar 

 

4、在安卓工程中加载 外部独立的dex文件。

      实现思路:把外部 独立的 dex文件 拷贝到 安卓项目的 assets 目录下,     app运行时 将 asstes 目录下的文件 通过 流式读取 复制到 应用的私有目录下。
    为什么要复制出来?因为assets目录下的文件并不能直接通过new File(xxx_path)的形式来访问, 它只能通过 Context.getAssets().open() 来获取一个访问流。    
       通过 DexClassLoader 读取 复制到app私有目录 下的 dex,调用 dexClassLoader.loadClass("Test") 获取 对应的 Class, 并通过构造函数创建找对象,再通过反射访问其内部的方法。

具体源码见下图

android 加载外部 dex文件中的类 的源码实例_第3张图片

 

题外话:如何将多个不同包路径 的class打包到同一个dex文件中

android 加载外部 dex文件中的类 的源码实例_第4张图片

我们先创建上图中的三个java源文件, 其中 Test.java 就是我们上面已经创建过的.

  创建第二个类文件,UserInfo.java

package cn;

/**
 * Created by meikai on 2018/08/03.
 */
public class UserInfo {

    public String userNmae;


    @Override
    public String toString() {
        return "用户姓名:" + userNmae;
    }
}

创建第三个类文件,Category.Java

package com.mike;

public class Category {

    public String channel;


    @Override
    public String toString() {
        return "频道:" + channel;
    }
    
}

 用javac 命令分别 生成对应的 class 文件, 如下图 

android 加载外部 dex文件中的类 的源码实例_第5张图片

 

这时如果直接像前面 打包 无包路径 的class那种试会失败,  报如下错误。 这个错误意思很好理解, 就是说 "类的名称" 和  "类的class文件的路径"  不匹配。
在java中  为方便源代码的管理,不是将所有的*.java源文件都放在一个文件夹下,  类的名称实际 由 类的包路径 和 类名称 综合决定,  class文件存放的位置也必须按其 包名 相同的路径。  这个应该在java基础中就有讲解。

android 加载外部 dex文件中的类 的源码实例_第6张图片

按上面的分析,我们分别建立对应其包路径的文件夹,将*.class文件分别放到对应的文件夹中, 再来执行 dx 命,  就能成功打包成一个  dex文件

android 加载外部 dex文件中的类 的源码实例_第7张图片

最后,我们用 dex2jar 工具,看看 对不对。  发现 这个  dex2jar  转换后的 jar 包 就有 这三个类

android 加载外部 dex文件中的类 的源码实例_第8张图片

 

 

彩蛋:补充介绍一下如果查看class文件对应的字节码

命令: javap -c *.class 

android 加载外部 dex文件中的类 的源码实例_第9张图片

 

你可能感兴趣的:(Android)