采集自动组合(\t统计数据APP开发一套数据采集SDK的功能:\页面访问流)
优采云 发布时间: 2021-10-31 19:16采集自动组合(\t统计数据APP开发一套数据采集SDK的功能:\页面访问流)
随着有货APP的不断迭*敏*感*词*发,数据和业务部门对客户用户行为数据的要求越来越高;为了更好的监控APP的使用状态,客户端团队对APP本身的运行有越来越多的数据需求。紧急发送。急需一套客户端数据采集的工具,自动完整的采集用户行为数据,满足各部门的数据需求。
\\
为此,物火APP团队开发了一套数据采集 SDK。主要功能如下:
\\ 页面访问流程。用户在使用APP的过程中浏览了哪些页面。\\t浏览数据公开。用户在某个页面上查看了哪些产品。\\t业务数据自动采集。用户在应用期间点击了哪些位置以及触发了哪些操作。\\t性能数据自动采集。用户在使用APP的过程中,页面加载时间多长,图片加载时间多长,网络请求时间多长等\
另外,所有数据采集应该是自动化的、非侵入性的,即不需要人工埋点,集成SDK即可使用,无需改动或改动原有代码可能的。
\\
基于以上需求,AOP是技术方案的最佳选择,而在iOS上实现AOP需要实现Objective-C中运行时的黑魔法Method Swizzle。踏坑填坑的漫漫征程从这里开始,让我们一一领略实现的思路和方法。
\\页面访问流程\\
用户访问页面统计需要解决两个问题:
\\ 统计事件的入口点,即何时进行计数。\\tStatistic 数据字段,统计哪些数据。\
整体流程如下:
\\
\\统计事件的入口\\
用户访问页面统计的大致思路在View Controller生命周期方法中:
\\
可以获得用户访问页面的路径,两个事件时间戳的差值就是用户在页面停留的时间。
\\
通常我们APP中的View Controller继承自某个基类。我们可以在基类的相应方法中进行统计。但是,对于不继承自基类的视图控制器,我们无能为力。
\\
借助AOP,我们可以更优雅地做到这一点:在UIViewController的load方法中混用viewDidAppear和viewDidDisappear方法,原代码无需改动。
\\统计字段\\
根据数据要求,设置以下统计字段:
\\
页面进入和退出事件在上述数据结构中上报。
\\
还有几个问题需要考虑:
\\1.如何定义PAGE_ID和SOURCE_ID\\
因为需要统一iOS和Android的PAGE_ID,所以需要配置下发。iOS端得到的是一个plist文件,文件的key是View Controller的类名的字符串表示,值为PAGE_ID。
\\2.如何获取PAGE_ID和SOURCE_ID\\
PAGE_ID可以直接根据当前View Controller的class获取。SOURCE_ID 稍微复杂一些。具体的获取方式需要根据APP页面的嵌套栈结构来确定。通常,可以从 UINavigationController 的导航堆栈中获取之前的 View Controller 的页面 id。.
\\
至此,页面访问流量统计基本完成,根据页面进入和退出的PAGE_ID和SOURCE_ID串出一条完整的用户浏览路径,得到用户在每个页面的停留时间。
\\浏览数据曝光\\
采集 用户的浏览路径,在每个页面停留的时间之后,在某些页面,比如首页和产品列表页面,我们也想知道用户在页面上刷了多少屏并阅读哪些活动和产品可以更好地为用户推荐喜欢的产品。
\\
用户看到的屏幕区域被认为是一个资源位,所以用户看到的内容是由资源位组成的。那么曝光的含义如下:
\\
我们知道iOS中页面元素的基本单位是视图,所以我们只需要判断视图是否在可见区域,就可以知道当前视图上的资源位置是否需要暴露,然后进行相应的曝光操作,采集数据,上报接口等。
\\
从上面的分析可以看出,主要有两个问题需要解决:
\\View的可见度判断\\tview曝光数据采集\View的可见度判断\\
查询UIView Class Reference可以看到setFrame:和layoutSubivews方法,可以用来设置subview的frame。每次更新视图名望时都会调用此方法。因此,我们可以通过runtime swizzle来实现这个方法,并添加一些数据采集相关的操作。
\\
我们向 UIView 添加了以下属性:
\\
首先,明确下列术语的定义和规则:
\\
1.view的子视图可以同时看到3个需要满足的条件:
\\
相反,只要不满足上述任一条件,我们认为该子视图目前是不可见的。
\\
2.将视图设置为可见
\\
3.将视图设置为不可见
\\
Swzzile setFrame:,执行以下操作:
\\
\\
Swzzile layoutSubivews,调用yh_updateVisibleSubViews方法,执行如下操作:
\\
\\
通过以上操作,我们可以知道某个视图及其子视图是否可见。
\\ 查看曝光数据采集\\
为了获取view对应的数据,UIView还添加了以下属性:
\\
那么有两个问题:
\\视图曝光数据的粒度\\tview及其子视图节点曝光数据的组装时序\
视图曝光数据的粒度
\\
根据项目中的实践经验,UITableViewCell或者UI采集ViewCell一般是粒度最小的。同时,在最后一个节点的yh_exposureData字典中,添加一个key:isEnd来标识是否是最后一个节点。
\\
组装视图及其子视图的曝光数据的时机
\\
一般当最后一个节点的可见性发生变化时,从下到上遍历最后一个节点的superview,组装所有数据。
\\
因此,我们覆盖了 setYh_viewVisible: 方法,也就是 yh_viewVisible 的 set 方法。请执行下列操作:
\\
至此,我们已经解决了视图可见性判断和曝光数据采集的问题。这里不再重复数据报告和策略。
\\
这个方案有几个缺点
\\ 需要手动设置曝光数据。\\t 需要在合适的时候手动调用view.yh_viewVisible来触发数据采集,比如viewdidappear。\\t 视觉区域计算和曝光数据需要消耗一定的资源采集。\
另外两个问题值得注意:
\\UITableView在setBounds:时会改变view frame,所以需要swizzle setBounds:方法,设置bounds后需要调用[self yh_updateVisibleSubViews];\\tUIScrollView在setContentInset:时会影响view的可见区域,所以它需要 swizzle setContentInset: 方法,设置 contentInset 后需要调用 self.yh_viewVisibleRect = UIEdgeInsetsInsetRect(self.frame, contentInset); \ 业务数据自动采集\\
业务数据自动采集,业界流行的无埋点数据采集。
\\
传统的客户端用户点击数据采集是基于人工埋点。如果您对哪个位置的数据感兴趣,只需单击此处。用户操作后,立即触发数据报表。人工掩埋的缺点很明显:错掩埋、漏掩。新版本发布后,经常有数据部的小伙伴反映某点的问题没有上报,某点的问题上报错了,开发同事也苦不堪言。
\\
无埋点数据采集带来新的变化。首先,基本避免人工埋葬,个别情况需要特殊处理。其次,从选择性的采集数据中,变成了采集用户点击和触摸的全量数据。
\\
新的变化也会带来新的挑战。未掩埋数据采集的可能性还是基于Objective-C的运行时特性。在实践过程中借鉴了iOS非埋点数据SDK的整体设计和技术实现,在实现中借鉴了Sensors Analytics iOS SDK和Mixpanel iPhone。接下来结合具体实践,介绍一下我们的实现思路和遇到的一些问题。主要分为以下三个方面:
\\ 如何保证自动采集的点的唯一性。\\t不同的点类型,需要哪些方法进行swizzle。坑在\\tswizzle的过程中踩到了。\ 如何保证自动采集的点数的唯一性\\
自动采集与手动埋点是分开的,所以没有该点的唯一标识。那么我们如何唯一定位自动采集的点呢?一种容易想到的解决方案是:基于页面视图的树状结构。这个解决方案可以分解为两个问题:
如何定义\\视图的唯一标识。\\tview 唯一标识如何生成它。\
视图唯一标识符(视图路径)的定义
\\
我们规定一个典型的视图路径如下:
\\
\ViewController[0]/UIView[0]/UITableView[0]/UITableViewCell[0:2]/UIButton[0]
\\
在:
\\ 有了这个标识符,就可以在当前页面的视图树结构中唯一标识这个元素。\\t 标识的每一项由两部分组成:一是当前元素所属类的字符串表示,二是当前元素在同级元素中的序号,从0开始计数。对于例如,当前第二个 UIImageView 是 UIImageView1。\\t 用 / 拼接标识不同的项目。\\t 标识的顶层是当前视图所在的 ViewController。\\t 对于UITableViewCell、UI采集ViewCell等类似的自定义组件,序号部分由section和row两部分组成,拼接在一起。\\t 标记的结尾是当前被点击或触摸的元素。\
如何生成视图唯一标识符
\\
视图路径生成过程:从触发操作的最末端元素向上查询,直到找到ViewController。假设当前点击的视图是A_View,从当前A_View开始遍历视图树,每一层的数据都存储在P_Array中。过程如下:
\\
\\ 如果A_View为UI采集ViewCell类型,获取A_View所在UI采集View的indexPath,P_Array推送路径信息[NSString stringWithFormat:@\"%@[%ld:%ld]\