58无埋点数据采集技术在Android端实践

2.1.2 页面事件

针对页面浏览事件,WMDA分两种不同的方式进行采集。

2.1.2.1 Activity采集  针对Activity,WMDA采用的方式是使用LifecycleCallback来监听页面的开启和关闭。  在页面开启时,拦截生命周期方法onResume,然后在事件处理模块处理,将其格式化成事件结构,并进行存储上报。

@Overridepublic void onActivityResumed(Activity activity) {    // 页面浏览事件采集处理    PageManager.getInstance().onActivityResumed(activity);}

不过该方案有个适配问题,在Android4.0(API14)以下,系统并不支持该方法。

注:目前App最低版本基本都是android 4.0及以上

在低版本中,我们也可以通过Hook方式拦截。通过拦截主线程的Instrumentation实例,来实现低版本页面的监听。这块同时还需要考虑第三方插件也Hook该实例的情况,执行Hook前对应方法,保证对app中其他插件没有影响。缺点是如果其他SDK也使用了这种方式,可能会影响我们的拦截。

@Overridepublic void callActivityOnResume(Activity activity) {    // 页面浏览事件采集处理    PageManager.getInstance().onActivityResumed(activity);    //执行Hook前Instrumentation实例的onResume方法    oldInstrumentation.callActivityOnResume(activity);}

2.1.2.2 Fragment采集  针对Fragment,由于Android系统并没有关于Fragment生命周期的回调监听,所以这里WMDA通过Gradle插件,在编译时期,利用ASM库进行字节码操作,对Fragment注入WmdaAgent相应的页面采集方法,完成事件采集。在注入策略上,我们只需要对Fragment父类为下面两个的页面注入采集代码即可。

android/app/Fragmentandroid/support/v4/app/Fragment

对Fragment相关方法注入代码示例:

@OverrideMethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);    MethodVisitor wrappedMv = mv;    if (wrappedMv != null) {        // 在onResume中插入WmdaAgent.onFragmentResumed方法        if (name.equals("onResume") && desc.equals("()V")) {            wrappedMv.visitCode()            wrappedMv.visitVarInsn(Opcodes.ALOAD, 0)            wrappedMv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/wuba/wmda/autobury/WmdaAgent",                    "onFragmentResumed", "(Landroid/app/Fragment;)V")        }    }}

WmdaAgent代码:

public static void onFragmentResumed(Fragment fragment) {    // 页面浏览事件采集处理    PageManager.getInstance().onFragmentResumed(fragment);}

2.1.3 控件点击事件

关于点击事件的采集,WMDA在早期研发过程中采取的是Mixpanel开源方案。在上文中已经提到过,该方案开发效率不错,不过性能问题、Fragment页面采集不到问题、版本适配问题,导致该方案存在瓶颈和风险。

在后续的持续探索中,我们发现,使用Gradle插件在编译时埋点可以完美继承Mixpanel方案的各项优点,同时又可以规避其性能、数据准确性和版本适配问题。于是,在控件点击事件的采集上,我们调整了技术实现方案,从动态对View设置代理演进为编译时插入埋点代码。

WMDA对点击事件拦截支持常用的一些第三方框架,比如:

ButterKnife、databinding、AndroidAnnotations、RxBinding

具体的技术和之前的Fragment插桩埋点是一样的,编译时对onClick方法注入代码,以AOP方式对事件拦截处理。核心实现思路如下:

插件代码

@OverrideMethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);    MethodVisitor wrappedMv = mv;    if (wrappedMv != null) {        // 查找出方法名为onClick,入参为View的方法,注入WmdaAgent.onViewClick(View view)        if (name.equals("onClick") && desc.equals("(Landroid/view/View;)V")) {            wrappedMv.visitCode()            wrappedMv.visitVarInsn(ALOAD, 1);            wrappedMv.visitMethodInsn(INVOKESTATIC, "com/wuba/wmda/autobury/WmdaAgent", "onViewClick", "(Landroid/view/View;)V", false);        }    }}

WmdaAgent对应代码:

public static void onViewClick(View view){    // 控件点击事件采集处理    AutoEventManager.getInstance().onEvent(view);}

2.1.4 自定义事件

无埋点是WMDA的核心功能,但是由于业务场景特点,无埋点并不能完全满足所有业务场景需求,所以WMDA也提供了对手动埋点支持,使得WMDA在实际的使用中更加灵活,数据统计也更全面。

这部分没有特别关键的技术点,就是普通的代码埋点,这里就不做过多介绍了。

2.2  事件处理

事件收集完成之后,就会发送到事件处理线程,对原生的事件进行加工,以便服务端能更好的进行分析处理。在页面事件处理中,我们将页面的class全路径作为页面的特征值。APP_PAGE示例:

com.wuba.wmdademo.MainActivity

采集到页面事件后将其传入子线程处理,然后再提取出业务开发人员在页面onCreate方法中设置的页面ID及页面自定义属性,将这些数据统一格式化,构造成页面浏览事件,传给事件存储模块。

在对控件事件处理中,我们面临一个最大的问题就是,**如何区分每一个控件,即如何定义控件的特征值。**在这里,我们借鉴了Mixpanel的方法,即将View自身的类名及index,以及其逐级向上的所有父View的类名和index统一收集起来,然后将所有遍历信息拼接起来,当做该View在当前页面的唯一特征值。控件的唯一标识:页面APP_PAGE + ViewPath + index

ViewPath举例:

/MainWindow/LinearLayout[0]/FrameLayout[1]/ActionBarOverlayLayout[0]#decor_content_parent/ContentFrameLayout[0]/LinearLayout[0]/ScrollView[0]/LinearLayout[0]/AppCompatButton

对于采集事件的后续处理,我们在UI性能上做了进一步优化。由于采用字节码插桩方式拦截事件,所以事件处理最耗时的点其实是在生成View特征值。在Android中,由于在子线程持有、操作view会引发内存泄漏问题。

在WMDA中,我们将构造特征值方法进行了拆分,在UI线程只进行对View数据提取,可以理解为定向的View遍历拷贝,除此之外不做任何其他耗时操作,然后将拷贝完成的ViewData传递给子线程,构造特征值,整合数据构造格式化的点击事件,最后再将事件传给存储模块。

点击事件处理时序图如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.3 事件存储

事件处理完成之后,要交由存储模块进行本地持久化。在存储之前,先会检查存储策略,满足策略后再进行存储。

存储这里,使用的是本地SQLite存储Protobuf实例的二进制,然后使用AES-256进行加密存储。

2.4 事件上报

事件存储完成之后,会触发上报请求。在上报之前,WMDA会先检查上报策略,满足策略后进行上报。

上报这里为了缩小WMDA包,只使用了HttpUrlConnection来处理网络操作。在数据上报的时候使用了GZIP+ProtoBuf来减少流量消耗,保证收集数据的同时,提升用户体验。

2.5 圈选模块

之前只是介绍数据采集方案,数据全量采集上报后,并不会直接分析处理,还需要一个圈选指标的过程。关于圈选的介绍,大家可以查看数据驱动增长:58无埋点用户行为分析实践之路 这篇的圈选部分,这里就不做重复介绍了。

通常,我们圈选时会在一个页面停留较长时间,这时其实是不需要一直将当前页面快照数据发送给服务端的,因为页面并没有变化。这块有一个优化策略,SDK会根据当前屏幕快照生成一指纹,只有当前屏幕有变更时才会将当前页面快照数据发给用户分析平台。

2.6 其他技术点

2.6.1 多进程数据采集

子进程中只存在事件采集事件处理两个模块,为了保证事件的连续性,数据的存储和上报则放到主进程来统一进行处理,这样也避免了数据库的同步问题,增加了数据的准确性,提升了系统性能。

因为事件采集是一种触发式的,所以我们在进程间通信上采用的是应用内广播,广播的优势是耦合度低,对子进程影响较小,同时性能相对来说可以接受。应用场景是技术选择的重要参考依据,所以这里并没有用Socket或者是AIDL来处理进程间通信。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.6.2 多进程界面圈选

考虑到圈选是一个实时、持续的过程,所以SDK采用Socket方式实现进程间通信,所有子进程都将页面快照信息发给主进程,由主进程和服务端交互。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
三、现存问题

当然,现阶段无埋点技术采用的字节码插桩方案还是存在一些短板,需要我们后续探索和解决。

  • click监听如果是在layout中使用android:onClick="xxxMethod"设置的暂时无法进行采集。这个设置监听的方法是利用Java的反射原理,去寻找对应的Method,在WMDA中是通过拦截OnClickListener点击事件来进行监听的,因此无法实现监听。
  • 由于目前采用的是编译时插入埋点,所以不支持目前比较流行的RN框架。
  • 同样因为是编译时插入埋点,所以对热更新的补丁支持可能也不到位。
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

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

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

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)

最后

都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。

技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 PDF(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

强太多

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值