关于方法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的方式代替方法名等信息,希望对你有帮助!

相关推荐
王码码203521 分钟前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
2501_9151063221 分钟前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
vistaup38 分钟前
OKHTTP 默认构建包含 android 4.4 的TLS 1.2 以及设备时间不对兼容
android·okhttp
常利兵44 分钟前
ButterKnife在Android 35 + Gradle 8.+环境下的适配困境与现代化迁移指南
android
撩得Android一次心动44 分钟前
Android LiveData 全面解析:使用Java构建响应式UI【源码篇】
android·java·android jetpack·livedata
熊猫钓鱼>_>1 小时前
移动端开发技术选型报告:三足鼎立时代的开发者指南(2026年2月)
android·人工智能·ios·app·鸿蒙·cpu·移动端
Rainman博11 小时前
WMS-窗口relayout&FinishDrawing
android
baidu_2474386113 小时前
Android ViewModel定时任务
android·开发语言·javascript
有位神秘人14 小时前
Android中Notification的使用详解
android·java·javascript
·云扬·14 小时前
MySQL Binlog落盘机制深度解析:性能与安全性的平衡艺术
android·mysql·adb