在一篇文章中,我们讨论了 安卓类加载机制分析 的理论知识, 这篇文章用一篇具体的实例来演示 插件化功能的 雏形。
类加载机制 是 研究插件化 和 热修复 的基础。 加载外部 dex文件中的类,我们在这部分主要做的流程有:
1、创建一个外部独立的java类文件
public class Test {
public void print() {
System.out.println("this is Test Class");
}
public String accessDex() {
return "这是一段存储在独立dex文件中的字符串";
}
}
2、用 javac 命令 生成 class 文件
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文件
同理,还可以使用此命令,将一个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>, 并通过构造函数创建找对象,再通过反射访问其内部的方法。
具体源码见下图
我们先创建上图中的三个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 文件, 如下图
这时如果直接像前面 打包 无包路径 的class那种试会失败, 报如下错误。 这个错误意思很好理解, 就是说 "类的名称" 和 "类的class文件的路径" 不匹配。
在java中 为方便源代码的管理,不是将所有的*.java源文件都放在一个文件夹下, 类的名称实际 由 类的包路径 和 类名称 综合决定, class文件存放的位置也必须按其 包名 相同的路径。 这个应该在java基础中就有讲解。
按上面的分析,我们分别建立对应其包路径的文件夹,将*.class文件分别放到对应的文件夹中, 再来执行 dx 命, 就能成功打包成一个 dex文件
最后,我们用 dex2jar 工具,看看 对不对。 发现 这个 dex2jar 转换后的 jar 包 就有 这三个类
命令: javap -c *.class