解决方案:Android 端埋点自动采集技术原理剖析

优采云 发布时间: 2020-09-14 08:06

  Android端埋点自动获取技术原理分析

  D

  本文的作者是阿里巴巴数据技术专家胡庆良。关注阿里巴巴数据,以了解有关数据技术的更多信息。编辑有话要说:本文主要是分析Andorid终端内部的自动采集技术的原理。

  在上一篇文章中,我介绍了我的团队。今天,我想继续介绍我们的Andorid内部自动采集

技术的原理分析。

  ◆◆◆

  定义

  APP嵌入点自动采集

是指自动采集

和报告APP中用户的操作行为并报告给日志。 APP中显示的元素(按钮,图片等)的行为主要分为点击行为和暴露行为。暴露是指元素停留在可见区域中的时间长度达到某个阈值,即,将其标记为暴露行为。本文的主要目的是分析Andorid终端内部的自动采集

技术的原理。

  ◆◆◆

  核心原则

  主流的Android事件监视机制主要包括四种监视方法:侦听器代理,Hook,AccessibilityDelegate和dispatchTouchEvent。下面将简要总结这四种方法的具体实现。 (本文没有介绍在AspectJ框架的编译期间通过注入代码进行监视的实现。主要原因是这种方法比较暴力并且业务过于侵入。在业务方面难以推广和实现。 APP。Google/百度。)

  2. 1侦听器代理

  在Android中,事件的监视和逻辑处理主要是通过覆盖View.onClickListener中的onClick方法来完成的,例如

  View saveView = findViewById(R.id.btnSave); saveView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //TO DO } });

  因此,您可以通过自定义监视代理类ProxyListener来在View.OnClickListener中实现onClick方法,并将控件的onClickListener替换为ProxyListener来完成单击监视和日志报告。代码如下:

  ProxyListener监视代理类:

  public abstract class ProxyListener implements View.OnClickListener{ @Override public void onClick(View view) { // doOnClick为业务方控件点击事件的逻辑实现 doOnClick(view); sendLog(view); } protected void sendLog(View view) { //TODO:detail of sendLog(), based on Thread Runnable runnable = new Runnable() { @Overrid public void run() { //TODO:do send log } }; Thread thread = new Thread(runnable); thread.start(); } protected abstract void doOnClick(View view);}

  对于所有控件,统一替换并调用监视代理类:

  View saveView = findViewById(R.id.btnSave);saveView.setOnClickListener(new ProxyListener() { @Override public void doOnClick(View v) { //TO DO }});

  2. 2挂钩机制

  钩子机制基于Java反射的原理。从rootview开始,它递归地遍历所有控件View对象,钩住其对应的OnClickListenr对象,并将其替换为用于报告日志的监视代理类ProxyListener,从而实现动态钩挂。实现代码如下:

  步骤1:创建监视代理程序管理类,以统一管理OnClickListenr对象的调用:

  public class ProxyManager { public static void sendLog(View view){} public static class ProxyListener implements View.OnClickListener{ View.OnClickListener mOriginalListener; public ProxyListener(View.OnClickListener l) { mOriginalListener = l; } @Override public void onClick(View v) { //TODO: send log sendLog(v); if(mOriginalListener != null) { mOriginalListener.onClick(v); } } }}

  第2步:创建一个反射管理类以保存挂钩的OnClickListener对象:

  public class HookView { public Method mHookMethod; public Field mHookField; public HookView(View view) { try { Class viewClass = Class.forName("android.view.View"); if(viewClass != null) { mHookMethod = viewClass.getDeclaredMethod("getListenerInfo"); if(mHookMethod != null) { mHookMethod.setAccessible(true); } } Class listenerInfoClass = Class.forName("android.view.View$ListenerInfo"); if(listenerInfoClass != null) { mHookField = listenerInfoClass.getDeclaredField("mOnClickListener"); } if(mHookField != null) { mHookField.setAccessible(true); } } catch (Exception e) {} }}

  第3步:递归地遍历所有控件,并为其替换OnClickListenr对象

  public void hookViews(View view) { try { if(view.getVisibility() == View.VISIBLE) { if(view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; int count = group.getChildCount(); for(int i=0; i结束曝光。其中,整个生命周期需要以基本的曝光规则为基础,即可见区域≥50%,可见时间≥500 ms才是合格的曝光。因此,一旦控件从不可见状态变为可见状态,我们将记录其当前可见状态和可见时间点的区域。当前控制树更改或触发控制扫描计时器时,需要调整现有的曝光控制。状态为已更新,可以在以下源代码中查看特定的更新规则:

  private void checkViewState(ExposureView exposureView, boolean status) { boolean needExposureProcess = isSatisfySize(exposureView.view); if (needExposureProcess) { switch (exposureView.lastState) { case ExposureView.INITIAL: //初始态需要处理,view的状态初始化 exposureView.lastState = ExposureView.SEEN; exposureView.beginTime = System.currentTimeMillis(); break; case ExposureView.SEEN: //当前控件依然可见,仅更新可见态控件当前的结束时间 exposureView.endTime = System.currentTimeMillis(); break; case ExposureView.UNSEEN: //不可见态,符合曝光条件,则初始化处理 exposureView.lastState = ExposureView.SEEN; exposureView.beginTime = System.currentTimeMillis(); break; default: break; } } else { switch (exposureView.lastState) { case ExposureView.INITIAL: break; case ExposureView.SEEN: //可见态,不符合界面曝光规则计算,则证明由可见态变为不可见,需要提交曝光数据 exposureView.lastState = ExposureView.UNSEEN; exposureView.endTime = System.currentTimeMillis(); break; case ExposureView.UNSEEN: //不可见态 break; default: break; } } if (exposureView.isSatisfyTimeRequired()) { if(status) { //页面切换,提交满足曝光条件的控件 addToCommit(exposureView); currentViews.remove(exposureView.tag); return; } if(exposureView.lastState == ExposureView.SEEN) { return; } else if(exposureView.lastState == ExposureView.UNSEEN) { addToCommit(exposureView); currentViews.remove(exposureView.tag); } } else if (exposureView.lastState == ExposureView.UNSEEN) { currentViews.remove(exposureView.tag); }}

  一旦曝光控件达到曝光时间限制和曝光区域限制,并且当前控件已从可见更改为不可见,则将提交缓存的曝光控制信息,并将调用集合SDK接口报告曝光日志。核心逻辑实现流程图如下:

  

  四、摘要

  自动获取和自动曝光技术有许多实现方法,但是每种实现类型也有很大不同。有必要根据特定的使用场景和自身的业务特点做出正确而正确的选择。

  本文仅介绍Android方面的技术原理。 IOS方面的实现有相似之处,请继续关注下一个问题。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线