Android一步步实现无痕埋点(5)-------斗转星移

0.前言

Android一步步实现无痕埋点(4)-------无尽痛苦

在之前全部,已经将无痕埋点transfrom的方面大概梳理了一遍.然后当你升级了gradle7.0之后.
你会发现transfrom有了删除线…他被抛弃了…(连同我的心也一样被抛弃了.)
全网资料真的很少,这还是看到虾哥这边比较新的用法
万恶之始
AsmClassVisitorFactory,我们来讲讲怎么用吧~拥抱变化吧…
在这里插入图片描述
我们实现的仍然是在setOnclick方法下,加入一个我们自己的方法.

1.Plugin

Plugin要变.
已经从registerTransform变成了transformClassesWith()
进入其注释可以知道这个方法是去注册一个 asm 类访问者以检测给定范围定义的类。 工厂的一个实例将被实例化并用于为每个类创建访问者。

class NewDoubleTapPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        //返回插件的类型
        val isApp = project.plugins.hasPlugin(AppPlugin::class.java)
        println("This is cool , you know -->$isApp")

        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.onVariants { variant ->
            variant.transformClassesWith(PrivacyClassVisitorFactory::class.java,
                    InstrumentationScope.ALL) {}

            variant.setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES)
            println("This is cool , you know -->END????")

        }
        return
    }

    companion object {
        private const val EXT_NAME = "NewdoubleTab"
    }

}

2.AsmClassVisitorFactory

createClassVisitor需要返回ClassVisitor类.
isInstrumentable。判断该类是否需要扫描,这里可以做过滤类的扫描.而ClassData则包含了类的一些信息,可以去看下定义


abstract class PrivacyClassVisitorFactory :AsmClassVisitorFactory<InstrumentationParameters.None>{
    override fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor {
        return PrivacyClassNode(nextClassVisitor)
    }
    //Is to scan all .class file
    override fun isInstrumentable(classData: ClassData): Boolean {
        return true
    }

}

3.ClassNode

下面替换的逻辑就不讲了.这里要知道的是ClassNode
它本身是有,interface,method…等属性的,我们需要找到的目标类都可以在这里进行寻找.
另外 ClassNode 因为是先生成的语法树,所以和一般的ClassVisitor有点小区别,需要在visitEnd方法内调用accept(next)


class PrivacyClassNode(private val nextVisitor: ClassVisitor) :ClassNode(Opcodes.ASM5) {

    //ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    //ClassVisitor methodFilterCV = new ClassFilterVisitor(classWriter);
    //ClassReader cr = new ClassReader(srcClass);
    //cr.accept(methodFilterCV, ClassReader.SKIP_DEBUG);

    override fun visitEnd() {
        super.visitEnd()
        val ClassInterface = interfaces
        println("privacy transform classNodeName: ${name@this}")

        ClassInterface?.forEach { Interface ->
            if (Interface == "android/view/View\$OnClickListener") {
                methods?.forEach { method ->
                    // 找到onClick 方法
                    if (method.name == "<init>") {
                        initFunction(this, method)
                    }
                    if (method.name == "onClick" && method.desc == "(Landroid/view/View;)V") {
                        insertTrack(this, method)
                        Log.info("Find the method ${method.name} and the desc ${method.desc}")

                    }
                }
            }
            }
        accept(nextVisitor)

    }


    private fun initFunction(node: ClassNode, method: MethodNode) {
        var hasLog = false
        node.fields?.forEach {
            if (it.name == "textLog") {
                hasLog = true
            }
        }

        if (!hasLog) {
            node.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, "textLog", String.format("L%s;",
                    "com/kotlin/aop/Buried_point/NewtextLog"), node.signature, null)
            val instructions = method.instructions
            method.instructions?.iterator()?.forEach {
                if ((it.opcode >= Opcodes.IRETURN && it.opcode <= Opcodes.RETURN) || it.opcode == Opcodes.ATHROW) {
                    instructions.insertBefore(it, VarInsnNode(Opcodes.ALOAD, 0))
                    instructions.insertBefore(it, TypeInsnNode(Opcodes.NEW, "com/kotlin/aop/Buried_point/NewtextLog") )
                    instructions.insertBefore(it, InsnNode(Opcodes.DUP))
                    instructions.insertBefore(it, MethodInsnNode(Opcodes.INVOKESPECIAL, "com/kotlin/aop/Buried_point/NewtextLog",
                            "<init>", "()V", false))
                    instructions.insertBefore(it, FieldInsnNode(Opcodes.PUTFIELD, node.name, "textLog",
                            String.format("L%s;", "com/kotlin/aop/Buried_point/NewtextLog")))
                }
            }
        }
    }


    private fun insertTrack(node: ClassNode, method: MethodNode) {
        // 判断方法名和方法描述
        val instructions = method.instructions
        val firstNode = instructions.first
        //Insert the code
        instructions?.insertBefore(firstNode, VarInsnNode(Opcodes.ALOAD, 0))
        instructions?.insertBefore(firstNode, FieldInsnNode(Opcodes.GETFIELD, node.name,
                "textLog", String.format("L%s;", "com/kotlin/aop/Buried_point/NewtextLog")))

        instructions?.insertBefore(firstNode, MethodInsnNode(Opcodes.INVOKEVIRTUAL, "com/kotlin/aop/Buried_point/NewtextLog",
                "textLog", "()V", false))
    }



}

4.总结

我们可以看到与transfrom的区别,我们不需要再去实现增量编译这种东西了。可以说这个东西算是对我们这种使用者能减少更多的学习成本了…当然新的东西没有那么被推广.
但是Android们都被推广Compose了.那使用AsmClassVisitorFactory也是非常合理的把~

不要在意有多长了…重要在于如何入门,然后再一步步的进阶,这些才是真的重要。

在这里插入图片描述

Android一步步实现无痕埋点(6)-------AutoService

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值