Dexmaker
一个用于在编译时或运行时针对Dalvik虚拟机进行代码生成的Java语言API。与cglib或ASM不同,该库生成Dalvik .dex
文件而不是Java .class
文件。
它有一个小巧、接近底层的API。这个API反映了Dalvik字节码规范,给你对生成的字节码有很强的控制。代码是逐条指令生成的; 如果需要的话,你可以带来自己的抽象语法树。由于它使用Dalvik的dx
工具作为后端,你可以免费获得高效的寄存器分配和常规/宽指令选择。
它的作用是什么?
Mockito Mocks
Dexmaker允许您在Android项目中使用Mockito模拟库,通过生成Dalvik字节码类代理来实现。只需在androidTestImplementation
依赖项中添加dexmaker-mockito
,您就可以在Android Instrumentation测试中使用Mockito。
Dexmaker针对的Mockito版本可以在dexmaker-mockito
的build.gradle文件中找到。一般规则是Dexmaker的主版本和次版本将匹配Mockito的主版本和次版本。
模拟最终类和方法
从Android "P"版本开始,可以使用dexmaker-mockito-inline
库模拟final类和方法。如果您在运行Android P或更高版本的设备或模拟器上执行测试,您可以在androidTestImplementation
依赖项中添加dexmaker-mockito-inline
(而不是dexmaker-mockito
;不要同时添加两者),并且可以使用普通的Mockito API在Android Instrumentation测试中模拟final类和方法。
**注意:**这个功能需要在Android P中引入的操作系统API,无法在旧版本的Android上工作。
类代理
Dexmaker包含一个用于类代理的标准代码生成器。 如果您只想进行AOP或类模拟,您不需要处理字节码。
运行时代码生成
这个示例生成一个类和一个方法。然后将该类加载到当前进程并调用其方法。
public final class HelloWorldMaker {
public static void main(String[] args) throws Exception {
DexMaker dexMaker = new DexMaker();
// 生成一个HelloWorld类。
TypeId<?> helloWorld = TypeId.get("LHelloWorld;");
dexMaker.declare(helloWorld, "HelloWorld.generated", Modifier.PUBLIC, TypeId.OBJECT);
generateHelloMethod(dexMaker, helloWorld);
// 创建dex文件并加载它。
File outputDir = new File(".");
ClassLoader loader = dexMaker.generateAndLoad(HelloWorldMaker.class.getClassLoader(),
outputDir, outputDir);
Class<?> helloWorldClass = loader.loadClass("HelloWorld");
// 在进程内执行我们新生成的代码。
helloWorldClass.getMethod("hello").invoke(null);
}
/**
* 生成等同于以下方法的Dalvik字节码。
* public static void hello() {
* int a = 0xabcd;
* int b = 0xaaaa;
* int c = a - b;
* String s = Integer.toHexString(c);
* System.out.println(s);
* return;
* }
*/
private static void generateHelloMethod(DexMaker dexMaker, TypeId<?> declaringType) {
// 查找我们沿途需要的一些类型。
TypeId<System> systemType = TypeId.get(System.class);
TypeId<PrintStream> printStreamType = TypeId.get(PrintStream.class);
// 确定declaringType上的'hello()'方法。
MethodId hello = declaringType.getMethod(TypeId.VOID, "hello");
// 在dexMaker上声明该方法。使用返回的Code实例
// 作为一个我们可以附加指令的构建器。
Code code = dexMaker.declare(hello, Modifier.STATIC | Modifier.PUBLIC);
// 提前声明我们需要的所有局部变量。API需要这样做。
Local<Integer> a = code.newLocal(TypeId.INT);
Local<Integer> b = code.newLocal(TypeId.INT);
Local<Integer> c = code.newLocal(TypeId.INT);
Local<String> s = code.newLocal(TypeId.STRING);
Local<PrintStream> localSystemOut = code.newLocal(printStreamType);
// int a = 0xabcd;
code.loadConstant(a, 0xabcd);
// int b = 0xaaaa;
code.loadConstant(b, 0xaaaa);
// int c = a - b;
code.op(BinaryOp.SUBTRACT, c, a, b);
// String s = Integer.toHexString(c);
MethodId<Integer, String> toHexString
= TypeId.get(Integer.class).getMethod(TypeId.STRING, "toHexString", TypeId.INT);
code.invokeStatic(toHexString, s, c);
// System.out.println(s);
FieldId<System, PrintStream> systemOutField = systemType.getField(printStreamType, "out");
code.sget(systemOutField, localSystemOut);
MethodId<PrintStream, Void> printlnMethod = printStreamType.getMethod(
TypeId.VOID, "println", TypeId.STRING);
code.invokeVirtual(printlnMethod, null, localSystemOut, s);
// return;
code.returnVoid();
}
}
下载
对于Mockito支持,通过Maven下载最新的.jar:
<dependency>
<groupId>com.linkedin.dexmaker</groupId>
<artifactId>dexmaker-mockito</artifactId>
<version>2.28.4</version>
<type>pom</type>
</dependency>
或Gradle:
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:2.28.4'
注意: Mockito的依赖关系将被传递包含,所以不需要同时指定Mockito和dexmaker-mockito
快照
您可以使用快照构建来测试最新的未发布更改。在将每个合并推送到主分支后,都会通过部署快照 Github Action 工作流发布一个新的快照。
只需将Sonatype快照存储库添加到您的Gradle脚本中:
repositories {
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
您可以在gradle.properties文件中找到最新的快照版本号。