关于方法id生成

什么是方法id

我们日常开发中,可能会遇到这么一个需求,就是想要把每一个方法,都转换为一个个Int类型/Long类型(推荐)来表示,这样的好处是效率非常高,比如在慢方法插桩里面,我们如果直接记录一个方法耗时,同时也要记录是哪个方法产生的耗时对吧,我们一般会以String类型方法名称去记录这么一条信息。但是以String可能会比较麻烦

原因在于String长度是不固定的,如果在插桩场景之下,如果需要插入的方法很多,那么它的消耗也是非常巨大的。

所以程序员们想啊想,既然字符串那么不可靠,那么如果我们能用一个数字代表一个方法,那么无论怎么变化,只要这个方法本身没有改变,那么是不是永远也能找到固定的值?这也通常被称为方法id

方法id生成

生成方法ID,最主要的是寻找一个映射关系,用于把方法映射成我们定义的ID,这里的ID可以是一个递增的数字,比如我们可以采用一个原子类型,比如AtomicInteger,每次遇到一个新方法的时候,这个数就递增即可,原子类型保证了我们能够在多线程环境下处理方法ID的正确生成,比如我们只需调用incrementAndGet即可处理并发问题。

那么还有一个问题,就是方法,我们怎么把一个方法跟一个id绑定起来呢?处理这个问题前,还有一个问题需要解决,怎么判断方法是不是同一个方法?

这里可以总结成一条公式:方法唯一值 = 方法名+方法签名+类特征

下面我们一一说明这几点:

方法名

一个方法的方法名,比如java/kotlin,就是一个方法的重要特征,当然,我们也不能仅仅依靠方法名去判断,因为java虚拟机是支持重载的,比如fun1(a:Int),跟fun1(a:Int,b:Int),虽然方法名一致,但是也不是同一个方法。因此需要另一个保证,方法签名

方法签名

代表着一个方法参数和返回值的特定字符串,比如一个fun1(param:String),它的签名就是(Ljava/lang/String;)V,括号中间代表着参数,右侧是返回值类型

以上两个,换算成字节码表示,以(ASM 中 MethodNode 为例子,方法名就是name属性,方法签名就是desc)

ini 复制代码
public class MethodNode extends MethodVisitor {
    public int access;
    方法名
    public String name;
    方法签名
    public String desc;
    public String signature;
    public List<String> exceptions;
    public List<ParameterNode> parameters;
    public List<AnnotationNode> visibleAnnotations;
    public List<AnnotationNode> invisibleAnnotations;
    public List<TypeAnnotationNode> visibleTypeAnnotations;
    public List<TypeAnnotationNode> invisibleTypeAnnotations;
    public List<Attribute> attrs;
    public Object annotationDefault;
    public List<AnnotationNode>[] visibleParameterAnnotations;
    public List<AnnotationNode>[] invisibleParameterAnnotations;
    public InsnList instructions;
    public List<TryCatchBlockNode> tryCatchBlocks;
    public int maxStack;
    public int maxLocals;
    public List<LocalVariableNode> localVariables;
    public List<LocalVariableAnnotationNode> visibleLocalVariableAnnotations;
    public List<LocalVariableAnnotationNode> invisibleLocalVariableAnnotations;
    private boolean visited;

通过方法名+方法签名,我们可以判断一个在类内的方法唯一值,注意这里特别表明是类内,因为如果同样的方法定义,在不同的类中,方法名与函数签名也是一致的,因此我们需要加入第三个维度,类特征

类特征

这里可以指代表一个类的唯一特征,比如我们常见的类名就是,由类加载验证过程,保证了类的唯一性,也可以是其他特有的信息。通过类特征,我们就能区别开不同类的方法

生成代码

通过上面的公式,我们就知道了一个方法唯一值,我们可以取类名+方法名+函数签名的方式拼凑而成,这个时候我们还需要一个容器,存储着两者的映射关系,这里我们可以用一个ConcurrentHashMap<String,Int>存储即可,这也我们可以在多线程环境下,也能收集到正确的方法id。

kotlin 复制代码
methodMap 是 ConcurrentHashMap<String,Int>,key是方法唯一值,value就是方法id类型
methodId 是一个AtomicInteger类型,用于原子的递增

fun methodCreate(){
    classNode.methods.forEach {
        if (!methodMap.containsKey(方法唯一值)) {
            设置方法id
            val id = methodId.incrementAndGet()
            methodMap[方法唯一值] = id
        }
    }
}

生成方法优劣

通过上文的生成方法,我们简单归纳一下好处与坏处

好处 坏处
可以直接在编译时简单生成,上面我们讲到的信息,都是可以在编译时获取到,比如 classNode对应着一个Transform/Transform Action过程的一个类,可以用ASM获取,生成也比较简单,可以支持多线程 只能处理编译时就确定的方法,因为依赖编译时生成id,对于动态生成的方法就没有办法

方法id生成实践

这种方法id生成思想,由于简单易用,所以也被很多大厂开源所采用,比如

MethodCollector

总结

通过本文,我们了解到了方法id以及常见的方法id生成过程,如果大家有多方法插桩的需求,不妨采用方法id的方式代替方法名等信息,希望对你有帮助!

相关推荐
inmK13 小时前
蓝奏云官方版不好用?蓝云最后一版实测:轻量化 + 不限速(避更新坑) 蓝云、蓝奏云第三方安卓版、蓝云最后一版、蓝奏云无广告管理工具、安卓网盘轻量化 APP
android·工具·网盘工具
giaoho3 小时前
Android 热点开发的相关api总结
android
咖啡の猫4 小时前
Android开发-常用布局
android·gitee
程序员老刘5 小时前
Google突然“变脸“,2026年要给全球开发者上“紧箍咒“?
android·flutter·客户端
Tans55 小时前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
雨白5 小时前
实现双向滑动的 ScalableImageView(下)
android
峥嵘life5 小时前
Android Studio新版本编译release版本apk实现
android·ide·android studio
studyForMokey8 小时前
【Android 消息机制】Handler
android
敲代码的鱼哇8 小时前
跳转原生系统设置插件 支持安卓/iOS/鸿蒙UTS组件
android·ios·harmonyos
翻滚丷大头鱼8 小时前
android View详解—动画
android