建材有限公司光速东莞网站建设,企业培训课程推荐,公司网页监控,photoshop网站视觉设计步骤首先什么是无埋点呢#xff0c;其实所谓无埋点就是开发者无需再对追踪点进行埋码#xff0c;而是脱离代码#xff0c;只需面对应用界面圈圈点点即可追加随时生效的事件数据点。 无埋点的好处 其实无埋点并不是完全不用写代码#xff0c;而是尽可能的少写代码。开发者将SDK集…首先什么是无埋点呢其实所谓无埋点就是开发者无需再对追踪点进行埋码而是脱离代码只需面对应用界面圈圈点点即可追加随时生效的事件数据点。 无埋点的好处 其实无埋点并不是完全不用写代码而是尽可能的少写代码。开发者将SDK集成到项目中配置并初始化SDK之后接下来就可以进行可视化操作。这个可以不依赖开发者一些实施人员都是通过后台的配制就达到埋点的配制还有新增埋点改动都是很方便的实现。最后就是配制和代码可以很灵活地扩展动态地更新。
全埋点采集的事件目前主要包括以下四种
$AppStart事件
指应用程序启动(冷启动和热启动)场景。热启动也就是指应用程序从后台恢复的情况。
$AppEnd事件
指应用程序退出 包括正常退出、按Home键进入后台、应用程序被强杀、应用程序崩溃等场景。
$AppViewScreen事件
是指应用程序页面浏览对于Android应用程序来说就是指切换Activity或Fragment。
$AppClick事件
是指应用程序控件点击也即View被点击比如点击Button、ListView等。 A p p C l i c k 是采集难度最大的事件全埋点的解决方案基本也是围绕着如何采集 AppClick是采集难度最大的事件全埋点的解决方案基本也是围绕着如何采集 AppClick是采集难度最大的事件全埋点的解决方案基本也是围绕着如何采集AppClick事件来进行的。
#AppClick事件的整体解决思路就是要找到那个被点击的控件处理逻辑然后再利用一定的技术原理对原处理逻辑进行拦截或者在原处理逻辑的执行前面或执行后面插入相应的埋点代码逻辑从而达到自动埋点的效果。
拦截的原理参考Android时间处理机制来进行。 插入的原理参考编译器对Java代码的整体处理流程来进行。 JavaCode - .java - .class - .dex选择在不同的阶段插入埋点代码所采用的技术或者原理也不尽相同。
$AppViewScreen全埋点方案
对于Activity就是onResume方法我们只要自动地在onResume里触发 A p p S c r e e n 事件即可解决 AppScreen事件即可解决 AppScreen事件即可解决AppViewScreen事件的全埋点。
关键技术:Application.ActivityLifecycleCallbacks
可提供全局Activity的监控。
$ AppStart、$AppEnd全埋点方案
归根结底就是判断当前应用程序是处于前台还是处于后台Android系统本身没有给应用程序提供相关的接口来判断这些状态。
应用程序如果有多个进程该如何判断是处于前台还是后台 ?
通过IPC机制实现数据共享
应用程序如果发生崩溃或者被强杀了该如何判断该应用程序是处于前台还是处于后台 ?
引入Session的概念:对于一个应用程序当它的一个页面退出了如果在30S之内没有新的页面打开我们就任务这个应用程序处于后台 (触发$AppEnd事件)。 当它的一个页面显示出来了如果与上一个页面的退出时间的间隔超过了30s我们就认为这个应用程序重新处于前台了 (触发了 $AppStart 事件)。
30s之内没有新的页面进来 (按了Home键/返回键退出应用程序、应用程序发生崩溃、应用程序被强杀)则会触发 A p p E n d 或者在下次启动的时候补发一个 AppEnd或者在下次启动的时候补发一个 AppEnd或者在下次启动的时候补发一个AppEnd事件。
$ AppClick全埋点方案1:代理View.OnClickListener
android.R.id.content
android.R.id.content对应的视图是一个FrameLayout布局它目前就只有一个子元素就是setContent时候的View。
需要注意 在不同的SDK版本下android.R.id.content所指的显示区域有所不同。
SDK 14(Native ActionBar):该显示区域指的是ActionBar下面的那部分。Support Library Revision lower than 19: 使用AppCompat则显示区域包含ActionBarSupport Library Revision 19(or greater):使用AppCompat则显示区域不包含ActionBar即与第一种情况相同。
原理概述
通过ActivityLifecycleCallbacks的onResume方法我们可以取到当前正在显示的Activity实例通过activity.findViewById(android.R.id.content)可以拿到id为content的这个FrameLayout然后再逐层遍历这个RootView并判断当前View是否设置了mOnClickListener对象如果已设置mOnClickListener对象并且mOnClickListener又不是我们自定义的WrapperOnClickListener类型则通过WrapperOnClickListener代理当前View设置的mOnClickLIstener。
引入DecorView
当前方案是无法采集MenuItem控件的点击事件的这是因为我们通过android.R.id.content取到的RootView是不包含Activity标题栏的也就是不包括MenuItem的父容器。 我们可以使用DecorView来解决
activity.getWindow().getDecorView()这样我们就可以遍历到MenuItem了。
Overridepublic void onActivityResumed(NonNull Activity activity) {new Handler().postDelayed(new Runnable() {Overridepublic void run() {delegateViewsOnClickListener(activity,activity.getWindow().getDecorView());}}, 300);}引入ViewTreeObserver.OnGlobalLayoutListener
当前方案还有一个问题无法采集onResume()生命周期之后动态创建的View点击事件。 可以通过ViewTreeObserver.OnGlobalLayoutListener来解决这个问题。
OnGlobalLayoutListener是ViewTreeObserver的一个内部接口。当一个视图树的布局发生变化时可以被ViewTreeObserver.OnGlobalLayoutListener监听到。
所以基于这个原理我们可以给当前Activity的RootView也添加一个ViewTreeObserver.OnGlobalLayoutListener监听器当收到onGlobalLayout方法回调时(即视图树的布局发生变化比如新的View被创建)我们重新去遍历一次RootView然后找到那些没有被代理过的mOnClickListener对象的View并进行代理即可解决上面提到的问题。
另外关于ViewTreeObserver.OnGlobalLayoutListener监听器建议在页面退出的时候remove掉即在onStop的时候调用removeOnGlobalLayoutListener方法。
由于该方案遍历的是Activity的RootView所以游离于Activity之上的点击是无法采集的比如Dialog、PopupWindow等。
可以采用代码埋点的方法辅助解决这个问题。
对于Dialog可以通过dialog.getWindow().getDecorView()拿到它的RootView然后手动触发遍历并代理即可。
public void trackDialog(final Activity activity,final Dialog dialog){if (dialog.getWindow() ! null) {dialog.getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {Overridepublic void onGlobalLayout() {SensorsDataPrivate.delegateViewsOnClickListener(activity, dialog.getWindow().getDecorView());}});}
}然后在Dialog创建之后show之前调用即可。
$ AppClick全埋点方案2:代理Window.Callback
Window.Callback是Window类的一个内部接口该接口包含了一系列类似于dispatchXXX和onXXX的接口。 当Window接收到外部状态改变的通知时就会回调其中的相应方法。 比如当用户点击某个控件时就会回调Window.Callback中的dispatchTouchEvent(MotionEvent event)方法。
原理概述
我们可以在ActivityLifecycleCallbacks的onActivityCreated方法回调中拿到当前正在显示的Activity对象通过activity.getWindow().getCallback()就可以拿到当前对应的Windo.Callback对象最后通过自定义的WrapperWindowCallback代理这个Window.Callback对象。然后在WraperWindowCallback的dispatchTouchEvent()方法中通过MotionEvent参数找到那个被点击的View对象并插入埋点代码最后再调用缘由Window.Callback的dispatchTouchEvent()方法即可达到插入埋点代码的效果。
缺点
每次点击时都要去遍历一次RootView所以效率相对来说较低对应用程序的整体性能影响也比较大。 无法采集像Dialog、PopupWindow等游离于Activity之外的控件的点击事件。
$ AppClick全埋点方案3:代理View.AccessibilityDelegate
Accessibility
Accessibility即辅助功能。
View.AccessibilityDelegate
View.performClick()源码中系统会先调用当前View已设置的mOnClickListener对象的onClick()方法然后再调用sendAccessibilityEvent()方法在sendAccessibilityEvent()方法的内部实现里其实是调用mAccessibilityDelegate对象的sendAccessibilityEvent方法并传入当前View对象和AccessibilityEvent.TYPE_VIEW_CLICKED参数。
所以我们只需要代理View的mAccessibilityDelegate对象当一个View被点击时在原有mOnClickListener对象的相应方法执行之后我们就能收到这个点击的回调。
原理概述
在ActivityLifecycleCallbacks的onActivityResumed方法中我们可以通过activity.getWindow().getDecorView()方法拿到当前Activity的RootView通过rootView.getViewTreeObserver()对象然后再通过addOnGlobalLayoutListener()方法给RootView注册ViewTreeObserverOnGlobalLayoutListener监听器这样可以在当前Activity的视图状态发生改变时去主动遍历一次RootView。 并且用我们自定义的WraperAccessibilityDelegate代理当前View的mAccessibilityDelegate对象。在我们自定义的WraperAccessibilityDelegate类中的sendAccessibilityEvent()方法实现里我们先调用原有的mAccessibilityDelegate对象的sendAccessibilityEvent方法然后再插入埋点代码其中host就是被点击的View对象从而可以做到自动埋点的效果。
缺点
辅助功能需要用户手动启动而且在部分ROM上辅助功能可能会失效。 无法采集Dialog、PopupWindow等游离于Activity之外的控件的点击事件。
$ AppClick全埋点方案4:透明层
该方案主要用到了Android系统事件处理机制方面的知识。
原理概述
onTouchEvent是在View中定义的一个方法用来处理传递到View的手势事件。 该方案就是基于View的onTouchEvent方法来实现的。
我们可以自定义一个透明的View然后添加到每个Activity的最上层。这样每当用户点击任何控件时直接点击的其实就是我们的这个自定义的透明View。 重写这个View的onTouchEvent方法就可以根据MontionEvent里的点击坐标信息(x,y)在当前Activity的RootView里找到实际上被点击的那个View对象。 找到被点击的View之后我们再通过自定义的WrapperOnClickListener代理当前View的mOnClickListener对象。 在WrapperOnClickListener的onClick方法里先调用View原有的mOnClickListener.onClick然后再插入埋点代码就能达到自动埋点的目的了。
缺点
无法采集Dialog、PopupWindow的点击事件 每次点击都要遍历一次RootView效率比较低
$ AppClick全埋点方案5:AspectJ
AOP是Aspect Oriented Programming 的缩写即面向切面编程。
AspectJ
AspectJ最核心的模块就是它提供的ajc编译器它其实就是将AspectJ的代码在编译器插入到目标程序当中。
自定义Gradle Plugin
原理概述
对于Android系统中的View它的点击处理逻辑都是通过设置相应的listener对象重写相应的回调方法实现的。 我们可以把AspectJ的处理脚本放到我们自定义的插件里然后编写相应的切面类再定义合适的PointCut用来匹配我们的织入方式 (listener对象的相应回调方法)比如android.view.View.OnClickListener的onClick(android.view.View)方法就可以在编译期间埋入埋点代码从而达到自动埋点的效果。
缺点
由于定义的切点依赖编程语言目前该方案无法兼容Lambda语法。
$ AppClick全埋点方案6:ASM
实现一套Transform去遍历所有.class文件的所有方法然后进行修改 (在特定listener的回调方法中插入埋点代码)最后再对原文件进行替换即可达到插入代码的目的。
关键技术
Gradle Transform是Android官方提供给开发者在项目构建阶段 (即由.class到.dex转换期间)用来修改.class文件的一套标准API。目前比较经典的应用是字节码插桩、代码注入等。
ASM
ASM是一个功能比较齐全的Java字节码操作与分析框架哦度会使用ASM我们可以动态生成类或者增强既有类的功能。
原理概述
我们可以自定义一个Gradle Plugin然后注册一个Transform对象。在transform方法里可以分别编列目录和jar包然后我们就可以遍历当前应用程序所有的.class文件然后再利用ASM框架的相关API去加载相应的.class文件、解析.class文件就可以找到满足特定条件的.class文件和相关方法最后去修改相应的方法以动态插入埋点字节码从而达到自动埋点的效果。
缺点
目前来看实现全埋点使用ASM框架是一个相对完美的选择暂时没有发现有什么缺点。
$ AppClick全埋点方案7:Javassist
Java字节码以二进制的形式存储在.class文件中每一个.class文件包含一个Java类或接口。Javaassist框架就是一个已经编译好的类中添加新的方法或者是修改已有的方法并且不需要对字节码方法有深入的了解。
Javassist可以绕过编译直接操作字节码从而实现代码的注入。 所以使用Javassist框架的最佳时机就是在构建工具Gradle将源文件编译成.class文件之后在将.class打包成.dex文件之前。
原理概述
在自定义的Plugin里我们可以注册一个自定义的Transform从而可以分别对当前应用程序的所有源码目录好jar包进行遍历。在遍历过程中利用Javassist框架的API可以对满足特定条件的方法进行修改比如插入相关埋点代码。 整个原理与使用ASM框架类似此时只是把操作.class文件的框架由ASM换成Javassist了。
$ AppClick全埋点方法8:AST
APT
APT (Annotation Processing Tool)即注解处理器是一种处理注解的工具。确切来说它是javac的一个工具用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入以生成.java文件作为输出。简单来说就是在编译器通过注解生成.java文件。
AST
AST是Abstract Syntax Tree的缩写即抽象语法树是编译器对代码的第一步加工之后的结果是一个树形式表示的源代码。源代码的每个元素映射到一个字节或子树。
Java的编译过程可以分为三个阶段: 第一阶段:所有的源文件会被解析成语法树。 第二阶段:调用注解处理器即APT模块。如果注解处理器产生了新的源文件新的源文件也要参与编译。 第三阶段:语法树会被分析并转化为类文件。
原理概述
编辑器对代码处理的流程大概是
JavaTXT - 词语法分析 - 生成AST - 语义分析 - 编译字节码通过AST可以达到修改源代码的功能。
在自定义注解处理器的process方法里通过roundEnvironment.getRootElements方法可以拿到所有的Element对象通过trees.getTree(element)方法可以拿到对应的抽象语法书(AST)然后我们自定义一个TreeTranslator在visitMethodDef里即可对方法进行判断。如果是目标处理方法则通过AST框架的相关API即可插入埋点代码从而实现全埋点的效果。
缺点
com.sun.tools.javac.tree相关API语法晦涩理解难度大要求有一定的编译原理基础。APT无法扫描其他module倒是AST无法处理其他module不支持Lambda语法带有返回值的方法很难把埋点代码插入到方法之后
本文主要是对Android无埋点技术的简答解析更多的Android核心技术可以参考《Android核心技术手册》点击可以查看详细的类目。