曝光埋点方案
发觉之前在工作中写的库还是被弄丢了。凭现在的回忆尽快复原下整体思路,以便后续需要时候参考。
思路
定义如下的工具入口方法:
sendExposureEvent(view, config)
现在因为kotlin的流行,可以改为View的拓展方法了,即View.sendExposureEvent(config)
内部会进行:view.setOnVibilityChangeListener(...)
,它也是一个拓展方法或者java的工具方法,其监听回调接口定义比如:
interface OnVisibilityChange{
void onChanged(Rect visibleRect, boolean isVisible, Boolean oldIsVisible)
}
OnVisibilityChange{ visibleRect, isVisible, oldIsVisible->
if(isVisible) lastVisibleTime = System.currentMileseconds();
if(!isVisble && System.currentMilseconds()-lastVisibleTime > exposureConfig.duration){
exposureConfig.eventTrigger.trigger()
}
}
setOnVisiblityChange
的实现则包含:
view.getVisbilityMonitor()//需要先获取一个和view所在Activity关联的Monitor
//Monitor若是初次创建,则要注册的事件包括:
// 它用于监测全局滚动事件,包括pager, recyclerview等
monitor.rootView.viewTreeObserver.addOnScrollChangedListener
// 它用于监测全局布局事件,包括可见性变动、节点操作如add/remove等
monitor.rootView.viewTreeObserver.addOnGlobalLayoutListener
// 它用于监测页面进入其他声明周期状态比如暂停等。
monitor.getAttachedActivity().registerActivityLifecycleCallbacks
// 在这些事件中,会遍历Monitor内部的监测列表
class VisibilityMonitor{
List<WeakReference<ViewTarget>> monitorViewList;
WeakRefence<Activity> activity;
class ViewTarget(WeakRefence view, VisibleState state, OnVisibilityChange eventListener)
void doCheckVisibility(){
pruneList();
for(ref in monitorViewList){
ref.get()?.apply{
isVisible = false
if(isShown() && getGlobalVisibleRect(rect)?.run{rect.width*rect.height / view.width / view.height > threshold}){
isVisible = true
}
if(isVisible!= state){
eventListener?.onVisiblityChanged(rect, isVisible, state)
state = isVisible
}
}
}
}
void pruneList(){
val newList = ArrayList()
for(ref in monitorViewList){
ref.get()?.apply{
newList.add(it)
}
}
setList(newList)
}
}
至此基本把监测的部分描述完了。除了这些,当时还有一个设计在于事件上下文。
埋点事件上下文
它依托于view.setTag
以及view的树形结构。在给定一个view节点需要曝光时,可以很方便地获取其所有父节点的View。由此,可以获得某些常量tag
的内容,这些tag
用于存储一些埋点上下数据会很有用。比如某个子view的埋点参数里需要父view、更更上级的page
view的数据,此时就可以借助埋点上下文的这个实现来解决。
fun View.findEventInfoContext(someDataClass: Class<T>):T?{
return getTag(EVENT_INFO_LIKE_MAP)[someDataClass]?:getParent().run{
when{
is View -> findEventInfoContext(someDataClass)
else null
}
}
}
大致模型如上。