环境:
Gradle 版本:
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
AGP版本:
buildscript { ext.kotlin_version = "1.6.20" dependencies { classpath "com.android.tools.build:gradle:7.2.2" } } plugins { id 'com.android.application' version '7.2.2' apply false id 'com.android.library' version '7.2.2' apply false id 'org.jetbrains.kotlin.android' version '1.7.10' apply false }
|
创建插件
1、插件配置
gradlePlugin { val tinker by plugins.creating { id = "tinker-plugin" implementationClass = "com.lyy.TinkerPlugin" } }
|
2、插件
class TinkerPlugin: Plugin<Project> { private val TAG = "TinkerPlugin" override fun apply(target: Project) { val androidComponentsExtension = target.extensions.getByType(AndroidComponentsExtension::class.java) androidComponentsExtension.onVariants { variant -> log("variant: ${variant.name}") variant.instrumentation.apply { transformClassesWith(TinkerTransform::class.java, InstrumentationScope.ALL) {} setAsmFramesComputationMode(FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS) } } } private fun log(msg: String) { println("[$TAG]: $msg") } }
|
AndroidComponentsExtension
:
是Android Gradle插件(AGP)提供的一个扩展点,它允许开发者在构建过程中访问Android应用程序组件(如Application、Library、Test等)。该扩展点提供了许多有用的信息,例如组件的名称、依赖项、构建类型、Flavor等。通过使用AndroidComponentsExtension,插件可以更加智能地操作应用程序组件。
transformClassesWith
:
相当于transform
,作用是将class转换为dex
InstrumentationScope.ALL
范围是所有的类,包括工程中的源代码和第三方依赖库的jar包,也就是说,我们不需要再手动处理jar的class了
InstrumentationScope.PROJECT
范围是当前工程中所有的类,不包括第三方依赖库jar包中的class
处理字节码
1、创建ClassVisitor
用于匹配需要修改的类
abstract class TinkerTransform : AsmClassVisitorFactory<InstrumentationParameters.None> { override fun createClassVisitor( classContext: ClassContext, nextClassVisitor: ClassVisitor ): ClassVisitor { return object : ClassVisitor(Opcodes.ASM5, nextClassVisitor) { val className = classContext.currentClassData.className override fun visitMethod( access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>? ): MethodVisitor { val oldMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions) if (className == "com.tencent.tinker.loader.SystemClassLoaderAdder" && name == "checkDexInstall" && descriptor == "(Ljava/lang/ClassLoader;)Z") { return TinkerVisitor(className, oldMethodVisitor, access, name, descriptor) } return oldMethodVisitor } } } override fun isInstrumentable(classData: ClassData): Boolean { return true } }
|
2、创建AdviceAdapter
修改方法的字节码
class TinkerVisitor( private val className: String, nextMethodVisitor: MethodVisitor, access: Int, name: String?, descriptor: String? ) : AdviceAdapter(Opcodes.ASM5, nextMethodVisitor, access, name, descriptor) { override fun visitInsn(opcode: Int) { println("visitInsn, name = ${name}, opcodeAndSource = ${opcode}") if (opcode == IRETURN){ println("开始修改返回") mv.visitInsn(ICONST_1) mv.visitInsn(IRETURN) return } super.visitInsn(opcode) } }
|
visitMethodInsn
是ASM的MethodVisitor类中的一个方法,用于访问方法调用指令。当ASM访问到字节码中的方法调用指令时,就会回调visitMethodInsn方法。
public void visitMethodInsn( int opcode, // 方法调用指令的操作码,如INVOKEVIRTUAL、INVOKESTATIC等 String owner, // 调用方法的类的全限定名 String name, // 调用方法的方法名 String descriptor, // 调用方法的描述符,如"(Ljava/lang/String;)V" boolean isInterface // 调用方法是否是接口方法 );
|
在visitMethodInsn
中修改method的指令,改方法无法修改方法的返回,如果要修改方法的返回,需要在visitInsn
中操作
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { if (opcode == Opcodes.INVOKESTATIC && owner.equals("java/lang/Math") && name.equals("random")) { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); super.visitInsn(Opcodes.DUP); super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(D)V", false); } else { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } }
|
override fun visitInsn(opcode: Int) { println("visitInsn, name = ${name}, opcodeAndSource = ${opcode}") if (opcode == IRETURN){ println("开始修改返回") mv.visitInsn(ICONST_1) mv.visitInsn(IRETURN) return } super.visitInsn(opcode) }
|
获取asm字节码
1、创建一个类,写一个测试方法
public fun test():Boolean{ return true }
|
2、使用as的插件直接看,右键
3、在右侧的插件那就可以看到需要的字节码了
效果
原来的:
修改字节码后的:
Vector Landscape Vectors by Vecteezy