Skip to content

曝光埋点方案

发觉之前在工作中写的库还是被弄丢了。凭现在的回忆尽快复原下整体思路,以便后续需要时候参考。

思路

定义如下的工具入口方法:

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、更更上级的pageview的数据,此时就可以借助埋点上下文的这个实现来解决。

fun View.findEventInfoContext(someDataClass: Class<T>):T?{
    return getTag(EVENT_INFO_LIKE_MAP)[someDataClass]?:getParent().run{
        when{
            is View -> findEventInfoContext(someDataClass)
            else null
        }
    }
}

大致模型如上。