IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 58无埋点数据采集技术在Android端实践 -> 正文阅读

[移动开发]58无埋点数据采集技术在Android端实践

作为国内最大分类信息生活服务平台,58集团旗下各个产品都会投入大量人力进行用户行为的分析,来提升运营效率。但是各个产品对用户行为的分析需求基本是相似的。在这样的背景下,我们自研了WMDA 无埋点用户行为分析平台,并提供对PC、M、APP三端支持,帮助各个业务线更好的挖掘用户真实行为。

对于SDK的使用,业务方不需要手动埋点,几行代码,即可实现数据的全量采集。对于移动端SDK来说,采集数据的准确性、及时性、全面性等因素直接决定后续用户行为的分析。本文将从技术选型、技术实现方案角度详细介绍Android端无埋点数据采集技术。

一、技术选型

首先,技术是为需求提供服务的,WMDA的定位是采用无埋点技术来实现用户行为的分析。同时辅助解决手动埋点不易维护,容易出现错埋、漏埋等痛点问题。所以SDK在采集用户行为数据的同时,对开发效率、采集性能、准确性、实时性等有很高的要求,而且需要支持数据的可回溯。

通过对市面上现有埋点技术调研,目前技术方案上大体分为三类:

  1. 传统代码埋点

    实现方案:Coding阶段手动埋点。

    代表解决方案:友盟、百度统计。

    优点:灵活、准确,可以定制化。

    缺点:业务埋点量非常大,开发成本高,不易维护,如果要修改、新增埋点,需要重新发版。

  2. 动态埋点

    实现方案:利用AccessibilityDelegate对每个view实例设置代理,监听控件点击事件。

    代表方案:Github上开源的Mixpanel

    优点:无需手动埋点,通过可视化圈选,动态下发配置监听指定控件。

    缺点:不支持数据可回溯,采集不到Fragment页面数据,只支持API 14及以上,同时该监听方式对app性能影响严重,每个控件都需要动态绑定,在界面变更时,需要重新刷新ViewTree,效率低下。

  3. 编译时字节码插桩埋点

    实现方案:利用Gradle插件,在编译阶段在代码中插入埋点代码,进行数据采集。

    代表方案:GrowingIO、美团的替换UI控件方案。

    优点:开发效率高,无需手动埋点,编译时插入代码,性能高,支持数据可回溯。

    缺点:埋点灵活性低。

通过以上简短分析,我们可以看出三种方案的优缺点都比较明显。最后,我们采取了利用Gradle插件自动注入埋点代码为主,并辅以手动埋点进行数据定制化补全的技术方案。

注:通过查看GrowingIO官方文档,GrowingIO现在也已提供对手动埋点的支持

二、技术实现

WMDA SDK Android端整体架构主要分为圈选模块、事件采集上报、配置管理三部分,如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4sKM6AF6-1630047215945)(https://user-gold-cdn.xitu.io/2018/6/16/16404a2f373de53b?imageView2/0/w/1280/h/960/ignore-error/1)]

下面根据事件采集上报流程分别来介绍事件采集、处理、存储、上报和圈选。

2.1 事件采集

WMDA移动端数据采集类型主要分三种:页面浏览事件、控件点击事件和自定义事件。作为无埋点解决方案,SDK核心点就是事件的无痕采集。 其中,这三种事件又对应不同的采集处理方式,WMDA通过不同的技术方案进行采集,最后将事件统一处理,然后存储、上报。

2.1.1 插桩入口

事件采集是无埋点技术的核心,其中WMDA对Fragment和控件点击事件拦截,使用的是自己开发的gradle插件wmda plugin,编译时使用ASM以字节码插桩的方式注入代码,实现事件的采集。

对于事件拦截,首先需要确定插入时机和待修改字节码文件。这里我们使用Transform API作为插桩入口,在Java Compiler之后,class文件打包成dex文件之前修改字节码文件。由于Transform API是在Gradle插件版本1.5.0出现的,所以项目开发中Gradle插件版本不能低于1.5.0。

`classpath 'com.android.tools.build:gradle:1.5.0'` 

然后在transform中遍历并操作字节码文件,因为现在很多大型项目,都会进行组件化操作,拆分成多个Library。所以这里除了要修改我们的应用源码,还需要对第三方库中的字节码文件进行扫描操作。

`void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {    inputs.each { TransformInput input ->        input.directoryInputs.each {            DirectoryInput directoryInput ->            //对应用源码生成的class操作        }        input.jarInputs.each { JarInput jarInput ->            //对第三方库中class操作        }    }}`

同时我们也引入了注入白名单机制,如果app不希望某些包名下的类被注入代码,比如使用的第三方sdk,可以在项目级别根目录下创建wmda-rules.properties文件并填写不希望注入的类路径。以下为示例,可以添加多个,每行一条,以 “/” 结尾:

`com/wuba/sdk/com/wuba/test/`

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传递给子线程,构造特征值,整合数据构造格式化的点击事件,最后再将事件传给存储模块。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZMbnN0e-1630047215946)(https://user-gold-cdn.xitu.io/2018/6/16/16404a2f374352e2?imageView2/0/w/1280/h/960/ignore-error/1)]

2.3 事件存储

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

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

2.4 事件上报

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

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

2.5 圈选模块

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

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

2.6 其他技术点

2.6.1 多进程数据采集

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mb5sEvJW-1630047215948)(https://user-gold-cdn.xitu.io/2018/6/16/16404a2f3741b6ba?imageView2/0/w/1280/h/960/ignore-error/1)]

2.6.2 多进程界面圈选

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JMJCatCS-1630047215948)(https://user-gold-cdn.xitu.io/2018/6/16/16404a2f5012ad9c?imageView2/0/w/1280/h/960/ignore-error/1)]

三、现存问题

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

  • click监听如果是在layout中使用android:onClick="xxxMethod"设置的暂时无法进行采集。这个设置监听的方法是利用Java的反射原理,去寻找对应的Method,在WMDA中是通过拦截OnClickListener点击事件来进行监听的,因此无法实现监听。

  • 由于目前采用的是编译时插入埋点,所以不支持目前比较流行的RN框架。

  • 同样因为是编译时插入埋点,所以对热更新的补丁支持可能也不到位。

四、总结

本文主要介绍了58无埋点数据采集技术在Android端实践。包括字节码插桩在无埋点的使用、对采集事件的处理等。同时现阶段无埋点技术还是存在一些问题需要我们后续探索和解决,欢迎感兴趣的同学和我们一起交流。

最后

按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。

整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。

图片

图片

因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。

如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。

(一)架构师必备Java基础

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

图片

(二)设计思想解读开源框架

1、热修复设计

2、插件化框架设计

3、组件化框架设计

4、图片加载框架

5、网络访问框架设计

6、RXJava响应式编程框架设计

……

图片

(三)360°全方位性能优化

1、设计思想与代码质量优化

2、程序性能优化

  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化

  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

……

图片

(四)Android框架体系架构

1、高级UI晋升

2、Android内核组件

3、大型项目必备IPC

4、数据持久与序列化

5、Framework内核解析

……

图片

(五)NDK模块开发

1、NDK开发之C/C++入门

2、JNI模块开发

3、Linux编程

4、底层图片处理

5、音视频开发

6、机器学习

……

图片

(六)Flutter学习进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter Dart语言系统入门

……

图片

(七)微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战

……

图片

(八)kotlin从入门到精通

1、准备开始

2、基础

3、类和对象

4、函数和lambda表达式

5、其他

……

图片

好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码领取哈~

img

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:27:50  更:2021-08-28 09:29:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 6:16:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码