- 使用 Core&Tree Api 读取 ArrayList
- 使用 Core&Tree Api 生成 Main 类
- 使用 Core&Tree Api 输出方法的入参和出参
- 使用 Core&Tree Api 删除方法里面的日志输出语句
- 使用 Core&Tree Api 输出特定方法耗时
- 使用 Core&Tree Api 统一线程重命名
- 使用 Core&Tree Api 给特定方法加上 try-catch 块
- 使用 Core&Tree Api 替换方法调用,为系统类或第三方库代码兜底
- 使用 Core&Tree Api 进行序列化检查
- 使用 Core&Tree Api 检查是否有调用不存在的方法
掌握最基础的 class 表现形式。
使用 ASM Api 生成以下 Main 类:
publicclassMain {
privatestaticfinalStringTAG = "Main";
publicstaticvoidmain(String[] args) {
System.out.println("Hello World.");
}
}修改前的 Java 文件:
publicclassPrintMethodParams {
publicStringprint(Stringname, intage) {
returnname + ": " + age;
}
}目标生成的类文件,且需要验证反射该 class 文件的正确性:
publicclassPrintMethodParamsCoreClass {
publicPrintMethodParamsCoreClass() {
}
publicStringprint(Stringvar1, intvar2) {
System.out.println(var1);
System.out.println(var2);
Stringvar10000 = var1 + ": " + var2;
System.out.println(var10000);
returnvar10000;
}
}修改前的 Java 文件:
publicclassDeleteLogInvoke {
publicStringprint(Stringname, intage) {
System.out.println(name);
Stringresult = name + ": " + age;
System.out.println(result);
System.out.println("Delete current line.");
System.out.println("name = " + name + ", age = " + age);
System.out.printf("name: %s%n", name);
System.out.println(String.format("age: %d", age));
returnresult;
}
}目标生成的类文件,且需要验证反射该 class 文件的正确性:
publicclassDeleteLogInvokeCoreClass {
publicDeleteLogInvokeCoreClass() {
}
publicStringprint(Stringvar1, intvar2) {
Stringvar3 = var1 + ": " + var2;
returnvar3;
}
}修改前的 Java 文件:
publicclassMeasureMethodTime {
@MeasureTimepublicvoidmeasure() {
try {
Thread.sleep(2000);
} catch (InterruptedExceptione) {
thrownewRuntimeException(e);
}
}
}目标生成的类文件,且需要验证反射该 class 的正确性:
publicclassMeasureMethodTimeCoreClass {
publicMeasureMethodTimeCoreClass() {
}
@MeasureTimepublicvoidmeasure() {
longvar1 = System.currentTimeMillis();
try {
Thread.sleep(2000L);
} catch (InterruptedExceptionvar8) {
RuntimeExceptionvar10000 = newRuntimeException(var8);
longvar4 = System.currentTimeMillis();
System.out.println(var4 - var1);
throwvar10000;
}
longvar6 = System.currentTimeMillis();
System.out.println(var6 - var1);
}
}修改前的 Java 文件:
publicclassThreadReName {
publicstaticvoidmain(String[] args) {
// 不带线程名称newThread(newInternalRunnable()).start();
// 带线程名称Threadthread0 = newThread(newInternalRunnable(), "thread0");
System.out.println("thread0: " + thread0.getName());
thread0.start();
Threadthread1 = newThread(newInternalRunnable());
// 设置线程名字thread1.setName("thread1");
System.out.println("thread1: " + thread1.getName());
thread1.start();
}
}目标生成的类文件,且需要验证反射该 class 的正确性:
publicclassThreadReNameCoreClass {
publicThreadReNameCoreClass() {
}
publicstaticvoidmain(String[] var0) {
(newShadowThread(newInternalRunnable(), "sample/ThreadReNameCoreClass#main-Thread-0")).start();
ShadowThreadvar1 = newShadowThread(newInternalRunnable(), "thread0", "sample/ThreadReNameCoreClass#main-Thread-1");
System.out.println("thread0: " + var1.getName());
var1.start();
ShadowThreadvar2 = newShadowThread(newInternalRunnable(), "sample/ThreadReNameCoreClass#main-Thread-2");
var2.setName(ShadowThread.makeThreadName("thread1", "sample/ThreadReNameCoreClass#main-Thread-3"));
System.out.println("thread1: " + var2.getName());
var2.start();
}
}修改前的 Java 文件:
publicclassCatchMethodInvoke {
publicintcalc() {
return1 / 0;
}
}目标生成的类文件,且需要验证反射该 class 的正确性:
publicclassCatchMethodInvokeCoreClass {
publicCatchMethodInvokeCoreClass() {
}
publicintcalc() {
try {
return1 / 0;
} catch (Exceptionvar2) {
var2.printStackTrace();
return0;
}
}
}这个任务的思路来源于:Booster 修复系统 Bug - Android 7.1 Toast 崩溃
其实延续上个任务的思路,为特定方法添加 try-catch 也能解决,但是 try-catch 过于粗暴,替换方法调用的方式灵活性更好些(其实在任务六里也有用到该思路)。
修改前的 Java 文件:
publicclassReplaceMethodInvoke {
publicstaticvoidmain(String[] args) {
// throw NPEnewToast().show();
}
}目标生成的类文件,且需要验证反射该 class 的正确性:
publicclassReplaceMethodInvokeCoreClass {
publicReplaceMethodInvokeCoreClass() {
}
publicstaticvoidmain(String[] var0) {
ShadowToast.show(newToast(), "sample/ReplaceMethodInvokeCoreClass");
}
}这个任务的思路来源于:ByteX - 序列化检查
对于 Java 的序列化,有以下几条规则需要遵守(也就是 IDEA Inspections 里的几条规则):
- 实现了 Serializable 的类未提供 serialVersionUID 字段
- 实现了 Serializable 的类包含非 transient、static 的字段,这些字段并未实现 Serializable 接口
- 未实现 Serializable 接口的类,包含 transient、serialVersionUID 字段
- 实现了 Serializable 的非静态内部类,它的外层类并未实现 Serializable 接口
针对以下代码:
publicclassSerializationCheckimplementsSerializable {
privateItemBean1itemBean1;
privateItemBean2itemBean2;
privatetransientItemBean3itemBean3;
privateStringname;
privateintage;
staticclassItemBean1 {
}
staticclassItemBean2implementsSerializable {
}
staticclassItemBean3 {
}
}检查输出:
Attention: Non-serializablefield'itemBean1'inaSerializableclass [sample/SerializationCheckCoreClass]
Attention: This [sample/SerializationCheckCoreClass] classisserializable, butdoesnotdefinea'serialVersionUID'field.这个任务的思路来源于:ByteX - 非法引用检查
不过示例里写的相对简单很多,怎么模拟引用找不到但编译不会报错的情况呢?我们可以借助于依赖方式:
compileOnly(project(":library1"))
runtimeOnly(fileTree("libs") { include("*.jar") })针对以下代码:
funmain() {
// throw IllegalAccessError:// class task_11.ReferCheckKt tried to access private method LibraryClass.testPrivateMethod()VLibraryClass().testPrivateMethod()
// throw NoSuchMethodError: LibraryClass.testModifyParamsMethod()VLibraryClass().testModifyParamsMethod()
// NoSuchMethodError: LibraryClass.testModifyReturnTypeMethod()VLibraryClass().testModifyReturnTypeMethod()
}检查输出:
在 sample/ReferCheckTreeClass$testPrivateMethod 方法里监测到非法调用: IllegalAccessError: 试图调用 private final LibraryClass.testPrivateMethod()V 方法.
在 sample/ReferCheckTreeClass$testModifyParamsMethod 方法里监测到非法调用: NoSuchMethodError: 找不到 LibraryClass.testModifyParamsMethod()V 方法.
在 sample/ReferCheckTreeClass$testModifyReturnTypeMethod 方法里监测到非法调用: NoSuchMethodError: 找不到 LibraryClass.testModifyReturnTypeMethod()V 方法.