埋点的背景
目前互联网/软件行业内,广泛使用数据驱动产品迭代,通过精细的数据分析、模型训练为用户提供更好的服务。在此过程中,数据埋点的工作是后续数据分析、模型训练等工作的基础。
数据埋点通常是产品经理、数据分析师,以及推荐系统工程师,基于业务需求(例如:广告的下载安装转化),产品需求(例如:关注按钮的曝光次数以及点击的人数)对用户行为的每一个事件确定埋点需求。客户端工程师进行对应的埋点功能开发,通过 SDK 上报埋点的数据结果,后端记录数据进行一系列处理,并汇总后提供给产品经理、数据分析师,以及推荐系统工程师进行数据分析或模型训练,帮助优化产品运营策略。
经典的消费场景
下面有几种经典的数据消费场景:

我们可以看到,行为分析埋点,需要包括某一事件发生时的前因、后果,以及事件发生对象的特征。在复杂的数据分析、模型训练等需求中,不仅仅需要获知某个事件的发生次数,对埋点上下文尤为关注。此处上下文指的通常有 2 类,分别是:
事件发生的页面信息和页面位置信息
用户经过怎样的路径来到当前页面,也就是“来源”信息
经典的埋点需求
下面我们结合具体场景,看 1 个简单的埋点需求,“点击收藏”事件

上面左图是西瓜放映厅的推荐列表,右图是某个影片的详情页,点击推荐列表的影片卡片,会跳转到详情页。作为最常见的消费场景,列表和详情都有收藏按钮,我们希望知道每一个收藏事件发生的场景,方便后续优化收藏功能,以及结合用户收藏的情况,优化推荐模型。
埋点需求是上报收藏按钮的点击事件 click_favorite,要求包含收藏影片的信息,所在的场景信息等。
如果收藏事件发生在列表页,会上报如下的内容
{
"event": "click_favorite",
"params": {
"video_id": "123", // 影片ID
"video_type": 2, // 影片类型
"page_name": "feed", // 当前页面
"tab_name": "long_video" // 当前所在的底Tab
"channel_name": "lvideo_recommend", // 当前所在的频道
}
}
如果收藏事件发生在详情页,会上报如下的内容
{
"event": "click_favorite",
"params": {
"video_id": "123", // 影片ID
"video_type": 2, // 影片类型
"page_name": "detail", // 当前页面
"from_page": "feed", // 来源页面
"from_tab_name": "long_video" // 来源底Tab
"from_channel_name": "lvideo_recommend", // 来源频道
}
}
现有方案
前端用户交互的界面,通常有复杂的页面层级关系和跳转逻辑,为了准确记录埋点信息,满足上述埋点需求,主要有以下几种实现方案。
直接传参
通过平台支持的参数传递方式,逐个定义并且读写参数;或者基于面向对象程序设计,对每个类添加相应的埋点参数,在类对象的关系中进行埋点参数传递。
对于上面的埋点需求 click_favorite,我们假设列表页和详情页的层级结构是:
列表页:CinemaTabFragment(放映厅 Tab)=> VideoChannelFragment(频道)=> VideoViewHolder(卡片)
详情页:VideoDetailActivity(详情页 Activity) -> BottomActionBar(底部操作栏)
当然实际情况因为项目的组件抽象复用等原因,往往会有更复杂的层级。直接传参要怎么实现这个埋点需求呢:
列表页的 click_favorite 埋点,需要从底 Tab 把所在 Tab 信息传给频道,频道再把底 Tab 和频道信息传给卡片
class CinemaTabFragment {
fun getItem() {
fragment = VideoChannelFragment()
// 配置频道所处的底Tab
fragment.tabName = "long_video"
return fragment
}
}
class VideoChannelFragment {
var tabName
var channelName
fun onBindViewHolder(position) {
holder.videoInfo = items.get(position)
// 配置卡片的tabName和channelName
holder.tabName = this.tabName
holder.channelName = this.channelName
}
}
class VideoViewHolder {
var tabName
var channelName
var videoInfo
fun clickFavorite() {
// 上报埋点的时候,拼接参数
LogSdk.onEvent("click_favorite", mapOf(
"tab_name" to this.tabName,
"channel_name" to this.channelName,
"video_id" to this.videoInfo.id,
"video_type" to this.videoInfo.type,
"page_name" to "feed"
))
}
}
详情页的 click_favorite 埋点,首先需要在列表页点击卡片跳转的时候,把上下文信息通过跳转参数传递给详情页,然后详情页解析出参数,传给底部操作栏
class VideoViewHolder {
var tabName
var channelName
var videoInfo
fun clickJumpDetail() {
intent.putExtra("from_tab_name", this.tabName)
intent.putExtra("from_channel_name", this.channelName)
intent.putExtra("from_page", "feed")
intent.putExtra("video_id", this.videoInfo.id)
startActivity(intent)
}
}
class VideoDetailActivity {
// 详情页还有其他埋点需要报这几个参数ÿ