#跟着坚果学鸿蒙#应用埋点开发实践<四>
·
曝光埋点
曝光埋点需要监测页面中每个组件的出现与消失。例如,当用户滑动瀑布流时,如果某个项目出现的时长超过500毫秒,则记录为一次有效曝光。为避免在每个页面注入冗长代码,建议使用自定义的“埋点钩子”组件进行封装。以下是具体实现步骤。
实现步骤
- 首先自定义一个TrackNode“钩子”组件,该组件需支持嵌套子组件、组件ID值注入以及注册监听事件。因此,TrackNode 中的 build 方法需由外部调用方决定,并且在 onDidBuild 生命周期中将组件信息注入。onDidBuild 主要完成以下三件事:
(1)调用TrackManager的addTrack方法,将当前组件与TrackShadow对象绑定。
(2)通过setOnVisibleAreaApproximateChange监听埋点组件的可视区域的变化;其中ratio值可以自定义设置,比如本示例设置了0.0、0.5、1.0。
(3)根据当前组件获取其父节点,判断父节点是否有埋点钩子。如果没有,继续往上追溯,直到父节点为 null。如果有,则在父节点的子组件集合中添加当前节点。
在aboutToDisappear生命周期中,调用TrackManager的removeTrack方法删除当前组件信息。
// entry\src\main\ets\viewModel\TrackNode.ets
// onDidBuild Life Cycle.
onDidBuild(): void {
// Construct the virtual tree of the tracing point.
// The obtained node is the root node of the current page (row in the test case).
let uid = this.getUniqueId();
let node: FrameNode | null = this.getUIContext().getFrameNodeByUniqueId(uid);
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `Track onDidBuild node:${node?.getNodeType()}`);
if (node === null) {
return;
}
this.trackShadow.node = node;
this.trackShadow.id = node?.getId();
this.trackShadow.track = this.track;
TrackManager.get().addTrack(this.trackShadow.id, this.trackShadow);
// The setOnVisibleAreaApproximateChange monitors and records the visible area of the tracing point component.
node?.commonEvent.setOnVisibleAreaApproximateChange(
{ ratios: [0, 0.5, 1], expectedUpdateInterval: 500 },
(ratioInc: boolean, ratio: number) => {
const areaChangeCb = CallbackManager.getInstance().getAreaChangeCallback();
areaChangeCb(node, ratio);
this.trackShadow.visibleRatio = ratio;
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `ratioInc: ${ratioInc}`);
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `ratio: ${ratio}`);
});
let parent: FrameNode | null = node?.getParent();
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `Parent getId: ${parent?.getId()}`);
let attachTrackToParent: (parent: FrameNode | null) => boolean =
(parent: FrameNode | null) => {
while (parent !== null) {
let parentTrack = TrackManager.get().getTrackById(parent?.getId());
if (parentTrack !== undefined) {
parentTrack.childIds.add(this.trackShadow.id);
this.trackShadow.parentId = parentTrack.id;
return true;
}
parent = parent.getParent();
}
return false;
};
let attached = attachTrackToParent(parent);
if (!attached) {
node?.commonEvent.setOnAppear(() => {
let attached = attachTrackToParent(parent);
if (attached) {
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `Track lazy attached: ${this.trackShadow.id}`);
}
});
}
}
- TrackManager封装了埋点钩子的操作方法,包括绑定、删除和导出。绑定是将当前组件ID与TrackShadow对象存入全局Map中。导出是以根节点为起点,递归输出所有子组件的曝光比例。删除是根据具体ID值从Map中移除对应数据。
// entry\src\main\ets\viewModel\TrackNode.ets
/**
* Tracing point data operation class
*/
export class TrackManager {
static instance: TrackManager;
private trackMap: Map<string, TrackShadow> = new Map();
private rootTrack: TrackShadow | null = null;
static get(): TrackManager {
if (TrackManager.instance !== undefined) {
return TrackManager.instance;
}
TrackManager.instance = new TrackManager();
return TrackManager.instance;
}
addTrack(id: string, track: TrackShadow): void {
if (this.trackMap.size === 0) {
this.rootTrack = track;
}
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `Track add id: ${id}`);
this.trackMap.set(id, track);
}
removeTrack(id: string): void {
let current = this.getTrackById(id);
if (current !== undefined) {
this.trackMap.delete(id);
let parent = this.getTrackById(current?.parentId);
parent?.childIds.delete(id);
}
}
getTrackById(id: string): TrackShadow | undefined {
return this.trackMap.get(id);
}
dump(): void {
this.rootTrack?.dump(0);
}
}
- TrackShadow对象包含FrameNode、track、childIds和parentId。FrameNode表示组件节点,track包含ID值,childIds表示子组件列表,parentId表示父组件的ID值。
// entry\src\main\ets\viewModel\TrackNode.ets
export class Track {
public areaPercent: number = 0;
public trackId: string = '';
constructor() {
}
id(newId: string): Track {
this.trackId = newId;
return this;
}
}
/**
* Tracing point data.
*/
export class TrackShadow {
public node: FrameNode | null = null;
public id: string = '';
public track: Track | null = null;
public childIds: Set<string> = new Set();
public parentId: string = '';
public visibleRect: common2D.Rect = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
public visibleRatio: number = 0;
// Output the information about the tracing point tree through global dump.
dump(depth: number = 0): void {
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `Track Dp: ${depth}`);
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `AreaPer: ${this.track?.areaPercent}`);
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `VisibleRatio: ${this.visibleRatio}`);
this.childIds.forEach((value: string) => {
TrackManager.get().getTrackById(value)?.dump(depth + 1);
});
}
}
- 根据上述TrackNode组件改造瀑布流代码:使用TrackNode钩子将WaterFlow和FlowItem组件包裹起来,并传递一个包含id的track对象,id作为组件的唯一标识。
// entry\src\main\ets\pages\WaterFlowPage.ets
TrackNode({ track: new Track().id('WaterFlow-1') }) {
WaterFlow() {
LazyForEach(this.dataSource, (item: number, index: number) => {
FlowItem() {
TrackNode({ track: new Track().id(`flowItem_${index}`) }) {
WaterFlowCard({ item: item, index: index }).id(`flowItem_${index}`)
}
}
// ...
}, (item: number) => item.toString())
}
.id('WaterFlow-1')
// ...
.onReachStart(() => {
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', 'waterFlow reach start');
})
.onScrollStart(() => {
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', 'waterFlow scroll start');
})
.onScrollStop(() => {
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', 'waterFlow scroll stop');
})
.onScrollFrameBegin((offset: number, state: ScrollState) => {
hilog.info(0x0000, 'ApplicationTrack', '%{public}s', `waterFlow scrollFrameBegin offset: ${offset}`);
hilog.info(0x0000, 'ApplicationTrack', '%{public}s',
`waterFlow scrollFrameBegin state: ${state.toString()}`);
return { offsetRemain: offset };
})
}
滚动瀑布流时,不仅可以监听每个Item的曝光比例,还可以追溯到根节点,统计根节点中每个子组件的曝光比例。
推荐内容
点击阅读全文
更多推荐
活动日历
查看更多
活动时间 2024-06-24 00:00:00

鸿蒙OS初体验:从0到1的开发者之路·苏州
活动时间 2024-06-11 00:00:00

鸿蒙OS初体验:从0到1的开发者之路·武汉
活动时间 2024-06-11 00:00:00

鸿蒙OS初体验:从0到1的开发者之路·长沙
活动时间 2024-06-11 00:00:00

鸿蒙OS初体验:从0到1的开发者之路·西安
活动时间 2024-06-11 00:00:00

鸿蒙OS初体验:从0到1的开发者之路·南京
社区排行榜
目录
所有评论(0)