作者 | doubimonkey
地址 | http://www.jianshu.com/p/419e7c25d1ba
声明 | 本文是 doubimonkey 原创,已获授权发布,未经原作者允许请勿转载
前言
目前在 app 上通过记录用户操作(俗称埋点),来分析用户行为的做法,已经成了 app 必不可少的一部分。有关 app 的埋点技术,也在发展中。正好最近项目组研发了一个埋点的 sdk,所以把相关知识梳理下。
埋点方式
代码埋点
这种方式主要是由程序猿们手动在代码中的回调事件里加上埋点代码。优点是高度定制,想怎么埋怎么埋,缺点是工作量大,而且易出错,难维护。
可视化埋点
这种埋点方式分为两种,一是使用后台界面配置需要埋点的位置,app下载配置文件,将需要埋点的事件上传(代表 MixPanel,百度,talkingdata等)。二是app把所有事件上传,后台自己选择需要埋点的点(代表heap)。
两种埋点方式各有优劣,但是由于技术目前还在发展中,并没有形成完全统一的理论以及方式,因此现在大多是这两种方式并存。
参考文献: http://blog.csdn.net/vshuang/article/details/60361314
MixPanel源码分析-Android
下面是分析 MixPanel 的源码,这应该是唯一的开源的商业埋点实现(其他我没找到),提供可视化埋点以及代码埋点方式。开源地址:https://github.com/mixpanel ,我主要是研究 Android 的代码。
本文的分析并不透彻,主要由于 mixpanel 的代码比较复杂,很多是和服务器的交互,在不了解协议的情况下,我也是连蒙带猜来看源码的。想透彻分析的同学,可以在 mixpanel 的网站上注册一个应用,再在应用里集成 mixpanel 的源码,然后加日志或者 debug 来分析。由于时间有限,我并没有这么做。请见谅。
首先是 mixpanel 的入口,MixPanelApi
该类中有大量的方法叫做 Tweak,这个是用来做 abtest 的,在服务器上做相应的配置,客户端可以拉取配置实现不同的功能。本文不讨论这个。
主要方法就是 track,
/**
* Track an event.
*
* <p>Every call to track eventually results in a data point sent to Mixpanel. These data points
* are what are measured, counted, and broken down to create your Mixpanel reports. Events
* have a string name, and an optional set of name/value pairs that describe the properties of
* that event.
*
* @param eventName The name of the event to send
* @param properties A JSONObject containing the key value pairs of the properties to include in this event.
* Pass null if no extra properties exist.
*/
public void track(String eventName, JSONObject properties) {
track(eventName, properties, false);
}
我们通过不停跟踪代码发现,这个方法会把埋点的 event,生成一个AnalyticsMessages.EventDescription 对象,然后通过 handler,发送到后台线程中去处理,代码如下
track(){
final AnalyticsMessages.EventDescription eventDescription =
new AnalyticsMessages.EventDescription(eventName, messageProps, mToken, isAutomaticEvent);
mMessages.eventsMessage(eventDescription);
}
// ...跳转至eventsMessage
public void eventsMessage(final EventDescription eventDescription) {
final Message m = Message.obtain();
m.what = ENQUEUE_EVENTS;
m.obj = eventDescription;
mWorker.runMessage(m);
}
//消息处理
if (msg.what == ENQUEUE_EVENTS) {
final EventDescription eventDescription = (EventDescription) msg.obj;
try {
//省略部分代码
returnCode = mDbAdapter.addJSON(message, token, MPDbAdapter.Table.EVENTS, eventDescription.isAutomatic());
} catch (final JSONException e) {
MPLog.e(LOGTAG, "Exception tracking event " + eventDescription.getEventName(), e);
}
}
可以看到,最终数据被存储到了数据库里,具体的数据库表结构大家可以自行看源码,我就不研究哦了。
那数据什么时候上传呢,主要是在 activiyt 的 onPause 之后上传。
@Override
public void onActivityPaused(final Activity activity) {
mPaused = true;
if (check != null) {
mHandler.removeCallbacks(check);
}
mHandler.postDelayed(check = new Runnable(){
@Override
public void run() {
if (mIsForeground && mPaused) {
mIsForeground = false;
try {
double sessionLength = System.currentTimeMillis() - sStartSessionTime;
if (sessionLength >= mConfig.getMinimumSessionDuration() && sessionLength < mConfig.getSessionTimeoutDuration()) {
DecimalFormat df = new DecimalFormat("#.0");
String sessionLengthString = df.format((System.currentTimeMillis() - sStartSessionTime) / 1000);
JSONObject sessionProperties = new JSONObject();
sessionProperties.put(AutomaticEvents.SESSION_LENGTH, sessionLengthString);
mMpInstance.track(AutomaticEvents.SESSION, sessionProperties, true);
}
} catch (JSONException e) {
e.printStackTrace();
}
mMpInstance.flush(); //上传
}
}
}, CHECK_DELAY);
}
用户也可以通过 MixPanelApi 的 flush 方法上传
public void flush() {
mMessages.postToServer(new AnalyticsMessages.FlushDescription(mToken));
}
这就是事件埋点的基本流程,当然功能不止这些,还可以通过 activity 的生命周期,记录页面停留时长等,这些都是基于这个基本流程来处理的。
可视化埋点
我觉得埋点主要的难点就是在可视化埋点上,如何做到良好的用户体验以及性能呢。我们一起来看看 MixPanel 是怎么做的。
首先看一下官网的介绍 https://mixpanel.com/autotrack/
通过视频可以看到,网页后台可以找到我们所有可以埋点的区域,该区域会高亮+边框显示出来,点击该区域,就会显示弹出一个对话框,就可以把这个区域射成一个埋点的位置。
这看起来是不是比代码埋点好多啦。那服务器是怎么找到 app 中可以埋点的位置的呢。我们来看一下源码,首先是连接上配置界面的地方,是通过 websocket 连接的,mixpanel 继承了大量 websocket 的实现,这里我们就不管他了,感兴趣的同学可以去自己研究下 websocket 的开源实现。具体处理协议的地方是 EditorClient 这个类
private class EditorClient extends WebSocketClient {
public EditorClient