美文网首页
ASM 无痕埋点

ASM 无痕埋点

作者: 馒Care | 来源:发表于2021-08-09 20:46 被阅读0次

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

ASM 能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。同时,ASM 与 Proxy 编程不同,不需要为增强代码而新定义一个接口,生成的代码可以覆盖原来的类,或者是原始类的子类。它是一个普通的 Java 类而不是 proxy 类,甚至可以在应用程序的类框架中拥有自己的位置,派生自己的子类。

2、字节码介绍

源代码中的各种变量,关键字和运算符号的语义最终都会编译成多条字节码命令。而字节码命令所能提供的语义描述能力是要明显强于Java本身的,所以有其他一些同样基于JVM的语言能提供许多Java所不支持的语言特性。


image.png

3、插件开发

(1)、可以创建一个plugin插件,来做字节码修改,以下是插件的目录:

  groovy目录,一般就是存放代码的目录,代码一般插件用groovy语言来编写,也可以用java,或者kotlin,因为他们最终都是变成字节码文件,可以互相兼容的,这里更推荐用java来写,groovy主要是不熟悉,kotlin的话会有很多意想不到的坑出现。

  resources目录的话,是用来存放plugin的命名等信息的,用来指定到具体的插件名字
image.png

(2)、创建插件主入口,一个plugin必须实现Plugin<Project>这个接口,否则此插件就不可用。

image.png

4、埋点

我们的埋点,主要是针对点击位,来做的,一般的点击位有,那我们需要埋点的地方有各个控件的click事件,比如text,button等。

一个textview可以有多种方式 去设置点击方式,比如setOnClick,xml中,rxview等,或者数据绑定的方式。在我们的项目中,通常我们设置一个控件的点击事件,我们可以这么设置


image.png

追踪到具体的binding中,可以发现里面其实是用rxview来做的绑定

image.png

接下来看rxview中做了什么事,实际上,他也是拿到view,去做了相对应的click事件

源码.png

5、因此,如果我们要把相对应的埋点入口埋入到这边的话,我们可以通过asm字节码来实现编译时注入代码。

在字节码中,方法通常会有几个属性,方法签名,方法名,我们可以通过这些属性,来找到我们所要匹配的方法,并埋入进去。比如下面的onclick方法,就有以下那些参数


image.png

在asm中,会对所有的方法进行扫描,然后在匹配到的方法中,都会进行方法的埋入,以达到埋点的效果,因为这个过程是不会修改到原始的代码逻辑,所以这个也是无侵入式的。

我们可以将一些控件的描述符和签名等信息收集起来,存放到map中,以便在asm扫描的时候,可以直接进行匹配。

上图的traceViewOnClick就是我们所要埋入的方法。以下是具体的代码

public static void trackViewOnClick(View view) {
        try {
            //获取Activity
            Activity activity = AutoTrackUtil.getActivityFromView(view);

            if (isDeBounceTrackForView(view)) {
                return;
            }
            JSONObject properties = new JSONObject();
            // 1、获取当前点击控件的全路径
            String viewPath = AutoTrackUtil.getViewPath(view);
            if (!TextUtils.isEmpty(viewPath)) {
                properties.put(LogConstants.AutoTrack.ELEMENT_VIEWPATH, viewPath);
            }

            // 2、获取Activity的标题名
            if (activity != null) {
                String activityTitle = AutoTrackUtil.getActivityTitle(activity);
                if (!TextUtils.isEmpty(activityTitle)) {
                    properties.put(LogConstants.AutoTrack.EVENT_SCAN_PAGE_TITLE, activityTitle);
                }
            }

            // 3、获取当前页面
            String screenName = activity.getClass().getSimpleName();
            if (!TextUtils.isEmpty(screenName)) {
                properties.put(LogConstants.AutoTrack.SCREEN_NAME, screenName);
            }

            // 4、获取ExpandableListView的控件名:ViewId
            String idString = AutoTrackUtil.getViewId(view);
            if (!TextUtils.isEmpty(idString)) {
                properties.put(LogConstants.AutoTrack.ELEMENT_ID, idString);
            }

            // 6、控件的类型
            properties.put(LogConstants.AutoTrack.ELEMENT_TYPE, view.getClass().getSimpleName());

            // 7、获取当前控件内容
            try {
                String viewText;
                if (view instanceof ViewGroup) {
                    StringBuilder stringBuilder = new StringBuilder();
                    viewText = AutoTrackUtil.traverseView(stringBuilder, (ViewGroup) view);
                    if (!TextUtils.isEmpty(viewText)) {
                        viewText = viewText.substring(0, viewText.length() - 1);
                        properties.put(LogConstants.AutoTrack.ELEMENT_CONTENT, viewText);
                    }
                } else {
                    CharSequence viewTextOnly = AutoTrackUtil.traverseViewOnly(view);
                    if (!TextUtils.isEmpty(viewTextOnly) && viewTextOnly != null) {
                        properties.put(LogConstants.AutoTrack.ELEMENT_CONTENT, viewTextOnly.toString());
                    }
                }

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

            // 8、获取 View 自定义属性
            JSONObject p = (JSONObject) view.getTag(R.id.auto_track_tag_view_properties);
            if (p != null) {
                AutoTrackUtil.mergeJsonObject(p, properties);
            }
            //执行上传操作
            VvTrackManager.getInstance().insertVvTrack2FireBaseAuto(properties, currentPageName, previewPageName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

通过当前的view可以获取到页面信息,包括控件的id,所属的页面等,因此可以直接上传这些数据。

相关文章

  • ASM 无痕埋点

    ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 c...

  • iOS无痕埋点方案分享探究

    iOS无痕埋点方案分享探究 iOS无痕埋点方案分享探究

  • 无痕埋点

    一、概念 通过技术手段无差别地记录用户在前端页面上的行为。可以正确的获取 PV、UV、IP、Action、Time...

  • ios 无痕埋点,两种方式

    FNKTrack 两种无痕埋点方式 FNKAopTrack 用Aspects进行AOP无痕埋点,具体见代码. FN...

  • 无痕埋点实践

    参考了一些实现方案,自己选择了一种适合自己项目的,这里我将进行总结。 主体思路 主要思路就是要通过方法交换实现。 ...

  • Asm初探

    最近项目中产品要求接入神策埋点,神策最大的宣传点应该就是所谓无痕全埋点。对于这种"无痕"或者"无感知",大部分An...

  • AOP无痕埋点技术

    使用AOP实现iOS应用内的埋点计数 - 简书 iOS用户行为追踪——无侵入埋点 - CSDN博客 iOS 无埋点...

  • 无痕埋点方案探究

    目前埋点的设计大致有以下几种:参考 网易HubbleData无埋点SDK在iOS端的设计与实现 1、代码埋点由开发...

  • 无痕埋点方案介绍

    参考1参考2参考3

  • 无痕埋点简单实现

    hook + aop 简单实现 1. View的层级 View 所有的Listener都存储在getListene...

网友评论

      本文标题:ASM 无痕埋点

      本文链接:https://www.haomeiwen.com/subject/luthmltx.html