目前统计打点已是一个产品常见的需求,尤为在业务模式探索的前期,埋点功能更是必不可少的功能,下面将介绍最简单的app全埋点方案!前端
数据埋点是通常项目采用统计UV,PV,Action,Time等一系列的数据信息,对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。android
产品或运营分析人员,基于埋点数据分析须要,对用户行为的每个事件进行埋点布置,并经过SDK上报埋点的数据结果,进行分析,并进一步优化产品或指导运营。
数据埋点包括哪些
这里有我以前写的一篇文章App优质精准的用户行为统计和日志打捞方案
地址:blog.csdn.net/sk719887916…git
自动埋点
App经过代理,调用Sdk相关API,进行的将数据埋点上报的模式.
无痕埋点
无需经过专门提供代理类,直接由sdk提供相关接口,或者经过编译工具,预编译替换代码等,直接由sdk所有负责采集上报
可视化埋点
可视化埋点指 前端或者app端基于dom 元素和控件所精准自动埋点的上报的方案。github
自动埋点:
缺点:
1 开发人员工做量大,需对业务提供惟一的ID,来区分每个业务,不管是否提供sdk代理,业务开发人员至少须要屡次调用sdk相关API.
2 业务人员和产品沟通成本提升,须要对具体业务制定相关的业务标识,以便于产品分析和统计
优势:
产品运营工做量少,对照业务映射表,就能分析出还原相关业务场景, 数据比较精细,无需大量的加工和处理。数据库
无痕埋点
缺点:
1 sdk开发人员需提供一套无痕埋点技术成品,包括能正确获取PV,UV,ACtion,TIme等多项统计指标。前期技术投入大。
2 数据量大,需后端落地进行大量处理,并由产品进行自我还原业务员场景。 不管采用智能系统平台,仍是经过原生的数据库查询数据,都是一种大量的分析精力。
优势:
1 开发人员工做量小,无需对业务标识进行惟一区分,由sdk自动进行生成,ID规则由sdk和产品进行约定。减小业务人员的沟通成本和使用步骤。
2 数据量全面,覆盖面广,产品可按需进行分析。作到毫无遗漏。
3 支持动态页面和局部动效的统计。编程
可视化埋点
优势:
1 相对数据量而言
相比较于无埋点相而言对较低,可是这个可视化元素的识别技术是客户端或者前端所要实现的,惟一id生成也无需客户端去自定义规则,这套生成规则由相关产品在自动化工具的状况下生成配置表,下发到客户端,再由客户端按坑就班到相关界面去实现。
2 数据量相对精确
缺点:
1 可视化工具的平台的搭建,静态页面的元素识别都须要额外开发。
2 动态效果可能会遗漏。json
埋点需求可参考我以前的文章:
App优质精准的用户行为统计和日志打捞方案:https://blog.csdn.net/sk719887916/article/details/50931485
App打造自定义的统计SDK:
https://www.jianshu.com/p/cd83e81b78aa
自动埋点实际上也是,提供一个base类,由业务类继承base类,在base里面作相关统计api调用,
可参考个人github:github.com/Tamicer/Sky…后端
android端打点的四个要素api
1 view展示,点击,消失
AccessibilityDelegate.
AccessibilityDelegate的使用(API level 14)
AccessibilityDelegate主要用来对view作一个检测,包括view的点击,选中,滑动,touch,文本变化及描述等等,能够用来作一些数据统计或者分析
建立一个自定义的AccessibilityDelegate,实现sendAccessibilityEvent(View host, int eventType)方法,而后经过view.setAccessibilityDelegate(),当该view的相关属性出现变化时,就会回调到实现的sendAccessibilityEvent方法中,咱们能够经过AccessibilityEvent.type_xxx来区分eventType是什么类型,而后作不一样的处理
给View设置AccessibilityDelegate,而当View 产生了click,long_click 等事件的时候.会在响应原有的Listener方法后.发送消息给
AccessibilityDelegate.而后在sendAccessibilityEvent方法下作打点操做.服务器
2 页面监听
ActivityLifecycleCallbacks
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {}
在onResume中对activity.getWindow().getDecorView().getRootView()进行向下遍历
3 定位View
View 的惟一性.
根据context的className+View自身的 Id的String 值.(String 值能够反射R文件获得)+View 的className值肯定.
当前没有id.向上须要父类的id,若是父类id 没有则记录父类className+getChildAt()值而且继续往上找,直到找到有id的view或者没有父类为止
4 上报时机
启动上报 轮询上报,网络变化,先后台切换,主动上报,推送回捞
以Android做为列子:
提供自动遍历元素 并能扑捉点击的控件的activity, 并能在生命周期统计pv的打开和关闭,调用我开源的SkyMonitoring的对应的api.
复写dispatchTouchEvent(MotionEvent ev) 事件函数,肯定被点击的view的相关位置,并生成惟一的ID,企业级app都是从服务器下发对应的ID,对应页面去调用埋点sdk Api,实现事件行为TcStatInterface.initEvent(path.viewTree);。
这个path就是view的路径,页面的深度路径,包括打开和关闭sdk在SkyMonitoring中已能自动获取。
本次demo是id生成规则是按照 :包名+ Activity+ Viewgroup+ Layout+ view + View index + viewID实现的。
业务直接去继承TamicActivity便可,就能去实现全部可视化view的埋点功能。
代码以下:
public abstract class TamicActivity extends AppCompatActivity {
private int statusBarHeight;
View rootView;
String rootViewTree;
String bigDataPrefix;
String bigDataIngorePrefix;
String bigDataEventPrefix;
private String TAG = "LYK";
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
//获取到根节点的view
rootView = getWindow().getDecorView();
//控件在视图树上的根路径
rootViewTree = getPackageName() + "." + getClass().getSimpleName();
//前缀名 bigData
bigDataPrefix = "Tamic_test";
//前缀名 bigData_
bigDataIngorePrefix = bigDataPrefix + "";
//前缀名 bigdata_ignore
bigDataEventPrefix = bigDataIngorePrefix +"Igmore";
}
@Override
protected void onResume() {
super.onResume();
TcStatInterface.recordPageStart(TamicActivity.this);
}
@Override
protected void onPause() {
super.onPause();
TcStatInterface.recordPageEnd();
}
@Override
protected void onDestroy() {
super.onDestroy();
// APP退出
TcStatInterface.recordAppEnd();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
ViewPath path = findClickView(ev);
if(path != null) {
Log.e(TAG, "path -->" + path.viewTree);
TcStatInterface.initEvent(path.viewTree);
}
}
return super.dispatchTouchEvent(ev);
}
private ViewPath findClickView(MotionEvent ev) {
Log.e(TAG, "bigdata-->findClickView");
ViewPath clickView = new ViewPath(rootView, rootViewTree);
return searchClickView(clickView, ev, 0);
}
private ViewPath searchClickView(ViewPath myView, MotionEvent event, int index) {
ViewPath clickView = null;
View view = myView.view;
if (isInView(view, event)) {
myView.level++;
if (myView.level == 2 && !"LinearLayout".equals(view.getClass().getSimpleName())) {
myView.filterLevelCount++;
}
if (myView.level > myView.filterLevelCount) {
myView.viewTree = myView.viewTree + "." + view.getClass().getSimpleName() + "[" + index + "]";
}
Log.i(TAG, "bigdata-->tag = " + view.getTag());
if (view.getTag() != null) {
// 主动标记不须要统计时,不进行自动统计
String tag = view.getTag().toString();
if (tag.startsWith(bigDataIngorePrefix)) {
return null;
} else if (tag.startsWith(bigDataPrefix)) {
if (tag.startsWith(bigDataEventPrefix)) {
myView.specifyTag = tag.replace(bigDataEventPrefix, "");
}
return myView;
}
}
if (view instanceof ViewGroup) {
if (view instanceof AbsListView) {
Log.i(TAG, "bigdata-->AbsListView ");
return null;
}
ViewGroup group = (ViewGroup) view;
int childCount = group.getChildCount();
if (childCount == 0) {
return myView;
}
for (int i = childCount - 1; i >= 0; i--) {
myView.view = group.getChildAt(i);
clickView = searchClickView(myView, event, i);
if (clickView != null) {
return clickView;
}
}
} else {
clickView = myView;
}
}
return clickView;
}
private boolean isInView(View view, MotionEvent event) {
if (view == null || view.getVisibility() != View.VISIBLE) {
return false;
}
int clickX = (int) event.getRawX();
int clickY = (int) event.getRawY();
int[] location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
int width = view.getWidth();
int height = view.getHeight();
return clickX > x && clickX < (x + width) && clickY > y && clickY < (y + height);
}
}
App项目集成使用,初始化url和相关统计配置字典,这个字典能够从服务器下发下来,我本次只是经过简单的本地文件作实践。
public class StatAppliation extends Application {
@Override
public void onCreate() {
super.onCreate();
// you app id
int appId = 21212;
// assets
String fileName = "my_statconfig.json";
String url = "https://github.com/Tamicer/TamicAppMonitoring";
// init statSdk
TcStatInterface.initialize(this, appId, "you app chanel", fileName);
TcStatInterface.setUrl(url);
TcStatInterface.setUploadPolicy(TcStatInterface.UploadPolicy.UPLOAD_POLICY_DEVELOPMENT, TcStatInterface.UPLOAD_TIME_ONE);
}
}
可视化也能够经过aop插桩实现,可是实现起来对代码的***性过高,这里不作介绍。
aop插桩对碎片化fragment支持比较好。对这块的介绍可看我之前在公众号推送的一篇文章:AOP编程之AspectJ实战实现数据无痕埋点
可参考:
https://www.baidu.com/link?url=FniQOFyj1pd6O5Fz6viRMN3ZgexIKAk7SQ08EgpBU9cHHMszPlm2jRXJ21mkomtY&wd=&eqid=ffc87acf0005fd18000000045a5d98dd
https://github.com/Tamicer/TamicAppMonitoring