编写ASM 代码操作字节码 并打印方法运行时间
package grg.app.buildsrc.method
import org.objectweb.asm.*
import org.objectweb.asm.commons.AdviceAdapter
/**
* 方法运行时间计时
* @param customTAG 自定义的打印tag
*/
class AddTimeListener(
val customTAG: String,
methodVisitor: MethodVisitor?,
access: Int,
name: String?,
descriptor: String?
) : AdviceAdapter(Opcodes.ASM7, methodVisitor, access, name, descriptor) {
val startTimeLabel = newLabel()
val endTimeLabel = newLabel()
var startTimeIndex: Int = 0
override fun onMethodEnter() {
super.onMethodEnter()
startTimeIndex = newLocal(Type.DOUBLE_TYPE)
startTimeLabel.let {
visitLabel(it)
}
mv.visitLocalVariable(
"startTime",
"J",
null,
startTimeLabel,
endTimeLabel,
startTimeIndex
)
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"android/os/SystemClock",
"currentThreadTimeMillis",
"()J",
false
)
println("插入本地变量表index=${startTimeIndex}")
mv.visitVarInsn(Opcodes.LSTORE, startTimeIndex)
}
override fun onMethodExit(opcode: Int) {
//求时间差 并存入 本地变量表
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"android/os/SystemClock",
"currentThreadTimeMillis",
"()J",
false
)
mv.visitVarInsn(Opcodes.LLOAD, startTimeIndex)
mv.visitInsn(Opcodes.LSUB)
mv.visitVarInsn(Opcodes.LSTORE, startTimeIndex)
//StringBuilder 构建str 并存入本地变量表
val strVarIndex = newLocal(Type.getType(String::class.java))
val strBeginLabel = newLabel()
val strEndLabel = newLabel()
mv.visitLocalVariable(
"__tempStr",
"Ljava/lang/String;",
null,
strBeginLabel,
strEndLabel,
strVarIndex
)
strBeginLabel.let {
visitLabel(it)
}
//NEW java/lang/StringBuilder
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder")
// DUP
mv.visitInsn(Opcodes.DUP)
// INVOKESPECIAL java/lang/StringBuilder.<init> ()V
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
"java/lang/StringBuilder",
"<init>",
"()V",
false
)
// LDC "Wtf"
mv.visitLdcInsn(customTAG)
// INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false
)
// ILOAD 2
mv.visitLdcInsn(",方法${name}消耗时间:")
// INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false
)
//求得的时间 入栈
mv.visitVarInsn(Opcodes.LLOAD, startTimeIndex)
//append 到 StringBuilder上
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(J)Ljava/lang/StringBuilder;",
false
)
// ILOAD 2
mv.visitLdcInsn("毫秒")
// INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false
)
//toString
// INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"toString",
"()Ljava/lang/String;",
false
)
mv.visitVarInsn(Opcodes.ASTORE, strVarIndex)
mv.visitFieldInsn(
Opcodes.GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;"
)
mv.visitVarInsn(Opcodes.ALOAD, strVarIndex)
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/String;)V",
false
)
endTimeLabel.let {
visitLabel(it)
}
strEndLabel.let {
visitLabel(it)
}
super.onMethodExit(opcode)
}
}
然后在ClassVisitor的visitMethod方法中对你想要的方法进行操作就行
eg:
class TestClassVisitor(classVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM6, classVisitor) {
val monitorMethodList = arrayOf<String>(
"onClick",
"onCreate",
"onDestroy",
"onResume"
)
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
val visitMethod = super.visitMethod(access, name, descriptor, signature, exceptions)
return when (name) {
in monitorMethodList -> {
AddTimeListener("TIME_TAG",visitMethod, access, name, descriptor)
}
else -> {
visitMethod
}
}
}
}
运行结果:
敲代码不易,加个收藏再走啦!!! 下一篇博客 我会逐一分析上述代码
代码分析已出炉 点这里立马看