实战:在每一个BaseActivity的子类的onCreate方法开头添加一些判断逻辑。
使用自定义插件的Transform利用ASM去修改代码后,查看反编译的字节码是实现步骤:
1. 编写自定义插件
因为本插件只给本工程用,就不用upload Task,直接创建一个buildSrc的java的module.
在App的build.gradle里面去应用插件 (apply plugin:'com.docwei.plugin') 即可。你们很疑惑这个插件名是哪里的插件名,其实插件名就是:com.docwei.plugin.properties这个properties文件的文件名。2. 拆解自定义插件的transform
- 1. 自定义插件需要创建一个类(比如:MyPlugin)去实现Plugin接口,重写里面的apply方法,其参数project即为应用了这个插件的project,这里是app的build.gradle应用了。
- 2. 字节码的处理:使用google提供的transform获取所有的字节码,修改字节码当然是使用ASM啦。 在Transform里面的输入类型有两种,一种是jarInput:包含本地和远端的jar,一种是directoryInput包含我们自己编写的代码,这里我们的MainActivity、SecondeAct,ThirdAct就是在这种输入类型里的。谨记:一旦注册了transform,就要处理输入和输出(默认实现是没有处理的),否则编译失败。
3. ASM的处理 (重点)
-
a. 先获取BaseActivity的所有子类
ClassReader就是利用io流读取字节码的包装类
Set<String> superClazzs = new HashSet<>();
superClazzs.add("com/docwei/transformwithasm/activity/BaseActivity");
directoryInput.file.eachFileRecurse { File file ->
if (file.isFile() && shouldProcessClassName(file.name)) {
FileInputStream fis = new FileInputStream(file);
ClassReader cr = new ClassReader(fis);
//接下来要过滤出BaseActivity的子类,然后去处理
if (hasImplSpecifiedInterfaces(cr, superNames)) {
byte[] bytes = scanClass(cr)
FileOutputStream fos = new FileOutputStream(file.parentFile.absolutePath + File.separator + file.name)
fos.write(bytes)
fos.close()
}
fis.close()
}
}
static boolean hasImplSpecifiedInterfaces(ClassReader reader, Set<String> superClazzs) {
if ("java/lang/Object".equals(reader.getClassName())) {
return false;
}
try {
if (superClazzs.contains(reader.getSuperName())) {
return true;
} else {
ClassReader parent = new ClassReader(reader.getSuperName());
return hasImplSpecifiedInterfaces(parent, superClazzs);
}
} catch (IOException e) {
return false;
}
}
-
b. 定位到子类的onCreate方法
static class ScanClassVisitor extends ClassVisitor {
ScanClassVisitor(int api, ClassVisitor cv) {
super(api, cv)
}
@Override
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name == 'onCreate' && desc == '(Landroid/os/Bundle;)V') {
// 先获取原始的方法
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions)
//再修改方法
return new ScanMethodVisitor(Opcodes.ASM6, mv)
}
return super.visitMethod(access, name, desc, signature, exceptions)
}
}
-
c. 在onCreate方法里面添加ASM字节码操作。
你可能觉得自己不会写Asm,不懂它的Api,不要紧,有工具,请速速安装:
最后:感谢在实现这个功能时抓头挠耳搜索到的所有相关博客,太多了就不贴链接了。
github项目链接 star 个呗。