58无埋点数据采集技术在Android端实践,2024年最新安卓面试宝典pdf

代表方案:Github上开源的Mixpanel

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

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

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

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

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

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

缺点:埋点灵活性低。

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

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

二、技术实现

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

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

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

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

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

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

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
img
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
img

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

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

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

写在最后

由于本文罗列的知识点是根据我自身总结出来的,并且由于本人水平有限,无法全部提及,欢迎大神们能补充~

将来我会对上面的知识点一个一个深入学习,也希望有童鞋跟我一起学习,一起进阶。

提升架构认知不是一蹴而就的,它离不开刻意学习和思考。

**这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家,**梳理了多年的架构经验,筹备近1个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

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

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

最近还在整理并复习一些Android基础知识点,有问题希望大家够指出,谢谢。

希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
间获取最新知识点

Android架构师之路很漫长,一起共勉吧!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-HWbbxrw9-1712764488382)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值