埋点系统的设计
- 插件化
- 回调统计
- 支持滚动列表的展示统计
- 支持时长统计
- 数据批量打包上传
- 完善的统计上传支持,中途断网数据不丢
1. 整体流程图
接口封装
/**
* @param event_type 事件类型
* @param c_id 事件Id(服务器定义)
* @param md moudleId
* @param is_anchor 是否立即上传
* @param args 附加参数
*/
public static void eventTongji(String event_type, int c_id, int md, int is_anchor, String args)
2. 插件
2.1 插件的初始化
public boolean initController(Context initContext)
2.2 插件统计入口
埋点数据在主App里面会把数据转成Json,然后以反射的形式调用插件里面UGCLoader的addUgcEvent方法
public void addEventUGC(final Context context, final String eventData)
然后会把把事件解析成 UGCEvent,根据实际情况需要需要回调Url的话会直接开始回调给第三方统计平台.最后事件会封装成LoadEventUGCRequest:
private static class LoadEventUGCRequest implements LoadRequest {
public TongjiEvent bean;
public LoadEventUGCRequest(TongjiEvent bean) {
this.bean = bean;
}
@Override
public void processRequest(UGCLoader dataLoader) {
}
}
常见的几种LoadRequest
类名 | 作用 |
---|---|
LoadEventUGCRequest | 打点事件 |
StoreUGCRequest | 存储事件 |
UploadEventUGCRequest | 强制上传事件 |
2.3 LoaderThread
事件加入阻塞队列loaderQueue后,负责取出事件,执行其中processEvent
while (true) {
try {
LoadRequest request = queue.take();
......
request.processRequest(loader);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.4 数据暂存和上传
数据取出来后悔有再次加入到等待上传队列eventList
当数据达到一定条件后执行上传操作
if (bean.is_anchor == 1 || eventList.size() >= PeacockController.UPLOAD_LIMIT_COUNT) {
//满30条上传或is_anchor立即上传不为0
UgcUploadManager manager = new UgcUploadManager();
ArrayList<TongjiEvent> uploadBeans = new ArrayList<TongjiEvent>();
uploadBeans.addAll(eventList);
eventList.clear();
manager.uploadLogUgc(mContext, uploadBeans);
}
上传流程
- 先判断EventLogTable是否有未上传的事件,有则加入到eventList,同时清空EventLogTable
- 再判断UuidDataCache是否有上传失败的报文,统一放入post参数中
- 然后把这条即将上传报文插入UuidDataCache
因此最后待上传的报文uploadData包含有:
[ currentUploadEventData,
faliedUploadEventData_0,
faliedUploadEventData_1
]
ps:最后的本地生成的UUID 其实没有作用
- 加密压缩上传
- 上传成功后删除UuidDataCache里面对应的UUID报文
3.2 埋点自查
- 一个系统级的浮层
- mock接口自查
直接mock这个中间数据解析,统计结果,方便单客户端自查
3.高阶封装
3.1 页面时长统计
在基类的onResume开始计时,在onPause时候停止计时,然后上报,即可统计本次展示时长
3.2 列表展示统计
在ListView Idle时候,递归循环遍历所有的子View,找出所有的TongjiLayout,然后调用其统计方法(统计数据已经预设进去了)
/**
* 获取ViewGroup里所有展现的 TongjiLayout(坑位)
* @param top 统计区间的顶部坐标
* @param bottom 统计区间的底部坐标
*/
public synchronized static void viewAllTongjiLayouts(ViewGroup group, int top, int bottom) {
try {
if (group == null) {
return;
}
if (group.getVisibility() == View.VISIBLE && group instanceof TongjiLayout) {
TongjiLayout layout = (TongjiLayout) group;
layout.tongjiView(top, bottom);
return;
}
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
View child = group.getChildAt(i);
if (child.getVisibility() == View.VISIBLE && child instanceof ViewGroup) {
if (child instanceof TongjiLayout) {
TongjiLayout layout = (TongjiLayout) child;
layout.tongjiView(top, bottom);
} else {
viewAllTongjiLayouts((ViewGroup) child, top, bottom);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
3.2.1 PV统计的限制条件
- 展示1/2之上
/**
* 检测是否该坑位坐标满足,漏出1/2即满足条件
*/
public boolean checkIsItemLocalRight(int top, int bottom) {
try {
int[] localtion = new int[2];
/**获取该View在屏幕中的位置*/
getLocationOnScreen(localtion);
//高和宽都显示大于一半则为true
return localtion[1] > top - getHeight()/2 && localtion[1] < bottom - getHeight() / 2&& localtion[0] > -getWidth() / 2 && localtion[0] < MidData.main_screenWidth - getWidth() / 2;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
- 10s内统计一次
String key = ad_item_id + "#" + md + "#" + pos + "#" + args + "#" + card_id;
if (ETADUtils.getItemTimeMap().containsKey(key)) {
return;
}
3.2.2 fragment的生命周期统计
参考文章
https://blog.csdn.net/tongcpp/article/details/41978751
https://www.jianshu.com/p/850556d33f63
同时也可以考虑统计View的可见性
参考文档:
Umeng统计设计: https://developer.umeng.com/docs/67953/detail/68140
埋点的设计问题
必须分模块,因为现在的组件都是相互集成,不知道,有可能在别的App集成了这个模块,这时候需要注意埋点参数必须有至少一个subCat表述所在位置
Moudle
SubMoudle
ItemId
EventType
InstantUpload
Properties