jhxlx
2022/09/29阅读:31主题:默认主题
Grade Plugin
Grade Plugin
字节码相关
java代码
javap后对应字节码

相关链接 字节码指令详解
Asm介绍
ASM 是一个 Java 字节码操作框架,它能用来动态生成类或者增强既有类的功能。ASM 可以直接生成二进制 class 文件,也可以在类被加载到 Java 虚拟机之前动态改变类行为。
ASM 框架中提供了常见的字节码分析和生成工具,可以快速进行类的生成或分析转换。
在 Android 开发中, Android Gradle 1.5 版本后提供的 Transform 机制, 它允许第三方的 Plugin 插件在 .class 文件打包成 dex 之前进行动态修改,这就为动态修改字节码文件提供了入口,衍生出很多“插桩”的功能,比如埋点、插入日志等。
核心Api
示例代码
val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS)
val classReader = ClassReader("com.andoter.asm_example.part2.ConvertDemo")
val classVisitor = ChangeVersionVisitor(Opcodes.ASM7, classWriter)
classReader.accept(classVisitor, ClassReader.SKIP_CODE)
1.ClassReader
用于将 Java 类文件转换成 ClassVisitor 能访问的结构。
2.ClassWriter
用于生成符合 JVM 规范的字节码文件,可以单独使用进行生成字节码文件,也可以配合 ClassReader 或 ClassVisitor 适配器进行现有类文件的修改。
3.ClassVisitor
用于访问class文件
主要方法 方法说明:
visit :访问类的头部,其中 version 指类创建时使用的 JDK 的版本,比如 50 代表 JDK1.6、51 代表 JDK1.7。access 代表类的访问权限,比如 public 、private。name 表示类名。signature 表示类的签名,如果类不是泛型或者没有继承泛型类,那么signature 值为空。superName 表示父类的名称。interfaces 表示实现的接口;
visitSource :访问类源文件;
visitModule:访问 Module 模块, Java9 中新增关键字 module用于定义代码和数据的封装体;
visitNestHost:访问嵌套类;
visitOuterClass:访问外部类;
visitAnnotation:访问类的注解;
visitTypeAnnotation:访问类的泛型签名的注解;
visitField:访问类的 Field 字段;
visitMethod:访问类的方法;
visitEnd:结束。
4.MethodVisitor
用于处理方法,获取方法信息
创建一个gradle plugin流程
新建java lib module

需要在resources下创建meta-inf文件夹,并创建对应的plugin.properties,名称一般和报名一致 plugin.properties内定义对应的插件

创建plugin

apply方法中注册transform,transform就是一个个gradle task 按插入顺序执行
*Transform类各个方法介绍
getName() :task名称
getInputTypes() :扫描类型,可设置扫描class和资源文件 getScopes() 设置扫描范围,可设置扫描jar aar,全部工程 isIncremental() 增量扫描
transform(TransformInvocation transformInvocation) 主要处理方法
在transform方法中transformInvocation可以获取编译生成的jar包和文件夹
Collection<TransformInput> inputs=transformInvocation.getInputs()
TransformOutputProvider outputProvider=transformInvocation.getOutputProvider()
if (outputProvider!=null) outputProvider.deleteAll()
//遍历inputs
inputs.each {TransformInput input ->
//遍历directoryInputs
input.directoryInputs.each { DirectoryInput directoryInput ->
handleDirectory(directoryInput,outputProvider)
}
//遍历jarInputs
input.jarInputs.each { JarInput jarInputs ->
handleJarInputs(jarInputs,outputProvider)
}
}
static void handleDirectory(DirectoryInput directoryInput,TransformOutputProvider outputProvider){
print('=====handleDirectory start')
if (directoryInput.file.isDirectory()){
directoryInput.file.eachFileRecurse {File file ->
def name=file.name
print('=====eachFileRecurse start'+file.absolutePath)
print('=====contains Activity'+name.contains("Activity.class"))
if (checkClassFile(name)){
print('----------- deal with "class" file <' + name + '> -----------')
ClassReader classReader=new ClassReader(file.bytes)
ClassWriter classWriter=new ClassWriter(classReader,ClassWriter.COMPUTE_MAXS)
ClassVisitor cv=new LifecycleClassVisitor(classWriter)
classReader.accept(cv,ClassReader.EXPAND_FRAMES)
byte[] code=classWriter.toByteArray()
FileOutputStream fileOutputStream=new FileOutputStream(file.parentFile.getAbsolutePath()+File.separator+name)
fileOutputStream.write(code)
fileOutputStream.close()
}
}
//Transform扫描的class文件是输入文件(input),有输入必然会有输出(output),处理完成后需要将输入文件拷贝到一个输出目录下去,
//后面打包将class文件转换成dex文件时,直接采用的就是输出目录下的class文件了。
//必须这样获取输出路径的目录名称
def dest= outputProvider.getContentLocation(directoryInput.name,directoryInput.getContentTypes(),directoryInput.scopes,
Format.DIRECTORY)
FileUtils.copyDirectory(directoryInput.file,dest)
}
}
LifecycleClassVisitor
private static class LifecycleClassVisitor extends ClassVisitor{
@Override
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor methodVisitor=super.visitMethod(access, name, desc, signature, exceptions)
print("calssname ========"+className)
if ("android/support/v4/app/FragmentActivity" .equals(className) ){
if ("onCreate".equals(name)){
print("LifecycleClassVisitor visit method "+name)
methodVisitor=new CreateMethodVisit(methodVisitor)
}else if ("onDestroy".equals(name)){
print("LifecycleClassVisitor visit method "+name)
methodVisitor=new DestroyMethodVisit(methodVisitor)
}
}
return methodVisitor
}
}
CreatMethodVisit
void visitCode() {
super.visitCode()
mv.visitLdcInsn("Tag")
mv.visitTypeInsn(Opcodes.NEW,"java/lang/StringBuilder")
mv.visitInsn(Opcodes.DUP)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/lang/StringBuilder","<init>","()V",false);
mv.visitLdcInsn("----- onDestroy: ")
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/lang/StringBuilder","append","(Ljava/lang/String;)Ljava/lang/StringBuilder;",false)
mv.visitVarInsn(Opcodes.ALOAD,0)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/lang/Object","getClass","()Ljava/lang/Class;",false)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/lang/Class","getSimpleName","()Ljava/lang/String;",false)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/lang/StringBuilder","append","(Ljava/lang/String;)Ljava/lang/StringBuilder;",false)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/lang/StringBuilder","toString","()Ljava/lang/String;",false)
mv.visitMethodInsn(Opcodes.INVOKESTATIC,"android/util/Log","i","(Ljava/lang/String;Ljava/lang/String;)I",false)
mv.visitInsn(Opcodes.POP);
}
作者介绍