当前位置:   article > 正文

滴滴DoKit Android核心原理揭秘之函数耗时,为什么Android要采用Binder作为IPC机制

dokit android

}

});

通过上文我们已经拿到了Application类的全路径名以及入口函数,那么接下来的操作就是查找attachBaseContex和onCreat中调用了哪些方法。其实ASM的AdviceAdapter这个类的visitMethod生命周期函数会在读取class文件流时输出当前函数的所有字节码(关于visitMethodInsn方法的具体用户可以参考官方文档,本文只会介绍相关原理),所以我们只需要根据自己的需要过滤出属于函数调用的部分就行。为了避免全量字节码插入带来的编译耗时过长问题,我限制函数插桩调用层级最大为5级。在每一级函数的遍历过程中,我们需要对函数的父级进行绑定。因为只有确定了父级函数,我们才能在下一次Transform中精准的知道需要在哪些子函数中进行代码插装。

函数调用栈查找代码:

@Override
public void visitMethodInsn(int opcode, String innerClassName, String innerMethodName, String innerDesc, boolean isInterface) {
//全局替换URL的openConnection方法为dokit的URLConnection

//普通方法 内部方法 静态方法
if (opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL) {
//过滤掉构造方法
if (innerMethodName.equals(“”)) {
super.visitMethodInsn(opcode, innerClassName, innerMethodName, innerDesc, isInterface);
return;
}

MethodStackNode methodStackNode = new MethodStackNode();
methodStackNode.setClassName(innerClassName);
methodStackNode.setMethodName(innerMethodName);
methodStackNode.setDesc(innerDesc);
methodStackNode.setParentClassName(className);
methodStackNode.setParentMethodName(methodName);
methodStackNode.setParentDesc(desc);
switch (level) {
case MethodStackNodeUtil.LEVEL_0:
methodStackNode.setLevel(MethodStackNodeUtil.LEVEL_1);
MethodStackNodeUtil.addFirstLevel(methodStackNode);
break;
case MethodStackNodeUtil.LEVEL_1:
methodStackNode.setLevel(MethodStackNodeUtil.LEVEL_2);
MethodStackNodeUtil.addSecondLevel(methodStackNode);
break;
case MethodStackNodeUtil.LEVEL_2:
methodStackNode.setLevel(MethodStackNodeUtil.LEVEL_3);
MethodStackNodeUtil.addThirdLevel(methodStackNode);
break;
case MethodStackNodeUtil.LEVEL_3:
methodStackNode.setLevel(MethodStackNodeUtil.LEVEL_3);
MethodStackNodeUtil.addFourthlyLevel(methodStackNode);
break;

case MethodStackNodeUtil.LEVEL_4:
methodStackNode.setLevel(MethodStackNodeUtil.LEVEL_3);
MethodStackNodeUtil.addFifthLevel(methodStackNode);
break;
default:
break;
}

}
super.visitMethodInsn(opcode, innerClassName, innerMethodName, innerDesc, isInterface);
}

字节码插桩代码:

@Override
protected void onMethodEnter() {
super.onMethodEnter();
try {
if (isStaticMethod) {
//静态方法需要插入的代码
mv.visitMethodInsn(INVOKESTATIC, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “getInstance”, “()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;”, false);
mv.visitIntInsn(SIPUSH, thresholdTime);
mv.visitInsn(level + ICONST_0);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(desc);
mv.visitMethodInsn(INVOKEVIRTUAL, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “recodeStaticMethodCostStart”, “(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V”, false);

} else {
//普通方法插入的代码
mv.visitMethodInsn(INVOKESTATIC, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “getInstance”, “()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;”, false);
mv.visitIntInsn(SIPUSH, thresholdTime);
mv.visitInsn(level + ICONST_0);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(desc);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “recodeObjectMethodCostStart”, “(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V”, false);

}
} catch (Exception e) {
e.printStackTrace();
}

}

@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode);
try {
if (isStaticMethod) {
//静态方法需要插入的代码
mv.visitMethodInsn(INVOKESTATIC, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “getInstance”, “()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;”, false);
mv.visitIntInsn(SIPUSH, thresholdTime);
mv.visitInsn(level + ICONST_0);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(desc);
mv.visitMethodInsn(INVOKEVIRTUAL, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “recodeStaticMethodCostEnd”, “(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V”, false);

} else {
//普通方法插入的代码
mv.visitMethodInsn(INVOKESTATIC, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “getInstance”, “()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;”, false);
mv.visitIntInsn(SIPUSH, thresholdTime);
mv.visitInsn(level + ICONST_0);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(desc);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, “com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil”, “recodeObjectMethodCostEnd”, “(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V”, false);
}
} catch (Exception e) {
e.printStackTrace();
}
}

运行时函数调用栈绑定

通过第三步我们已经在适当的函数中插入了AOP模板耗时统计代码,但是最终还是需要在代码运行期间才能统计出具体的函数运行耗时,并对函数调用做上下级绑定才能最终呈现出友好的数据展示。

由于在编译期间我们已经知道了函数的上下级关系,并且将每个函数的调用等级通过方法参数的形式插入了AOP模板中,所以接下来我们只需要在函数运行期间对每一级的函数进行分类保存,并通过适当的算法绑定上下级关系即可。由于函数上下级绑定代码过长大家可以通过点击以下代码直接查看:MethodStackUtil

最终效果

经过以上的四步操作,我们已经实现了我们一开始的需求,下面我们就一起来看下最终的效果:

默认方案

场景一:App启动

DoKit

场景二:耗时方法

private fun test1() {
try {
Thread.sleep(1000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
test2()
}

private fun test2() {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

结语

  • 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
  • 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
  • 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。

而且

  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。

[外链图片转存中…(img-1FE9GSRa-1711879475541)]

[外链图片转存中…(img-fkCRqhac-1711879475542)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/726154
推荐阅读
相关标签
  

闽ICP备14008679号