📚往期笔录记录📔:
- 鸿蒙(HarmonyOS)北向开发知识点记录~
- 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
- 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
- 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
- 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
- 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
- 记录一场鸿蒙开发岗位面试经历~
- 持续更新中……
概述
埋点是指将信息采集程序和原本的功能代码结合起来,针对特定用户行为收集、处理和发送一些信息,用来跟踪应用使用情况。包括访问数、访客数、停留时长、页面浏览数和跳出率。以下是几种常见业务场景:
- 页面中可视区域或者组件的点击量,统计点击频率,分析用户的偏好行为。
- 监听页面中组件滑动的开始与结束,计算滑动偏移量以及曝光比例。
- 监听页面切换,统计页面的停留时间以及切换的来源页和目标页,分析页面浏览数和跳出率。
- 分析页面加载性能,计算加载过程各个节点的耗时,可针对某个关键点进行优化。
埋点分类
按照用户行为不同,埋点可以分为点击埋点、曝光埋点以及页面埋点等。
- 点击埋点:用户在任意区域的一次点击,比如一个icon或一张图片。区别于被动的用户曝光行为,点击属于主动行为。
- 曝光埋点:统计页面局部区域是否被用户有效浏览,比如瀑布流中的每一个卡片的曝光比例以及曝光时长,属于被动行为。
- 页面埋点:统计用户在固定页面的停留时间,页面加载性能以及页面跳转时的来源页和去向页信息。
方案介绍
接下来会从:
(1)组件动态绑定埋点数据;(2)点击埋点方案;
(3)曝光埋点方案;
(4)页面埋点方案四部分介绍。整体方案使用全局无感监听能力 UIObserver 和 setOnVisibleAreaApproximateChange 属性实现埋点功能。
绑定埋点数据
首先针对需要埋点的组件指定对应的ID值以及埋点数据。比如Text组件可以指定ID为“text-1”,并且通过customProperty自定义属性设置key和value,key为组件ID,value为埋点数据。为方便拿取,可以将埋点数据统一定义在DataResource中。
import { DataResource } from '../common/DataResource';
import { router } from '@kit.ArkUI';
@Entry
@Component
struct Index {
onPageShow(): void {
const uiContext: UIContext = this.getUIContext();
console.log('text-1', JSON.stringify(uiContext.getFrameNodeById('text-1')?.getCustomProperty('text-1')));
}
build() {
Column() {
Text('text-1')
.id('text-1')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.customProperty('text-1', DataResource['Index']['text-1'])
.onClick(() => {
console.log('111111');
})
Text('text-2')
.id('text-2')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.customProperty('text-2', DataResource['Index']['text-2'])
.onClick(() => {
})
Button('Navigation')
.onClick(() => {
router.pushUrl({ url: 'pages/NavigationPage' })
})
Button('WaterFlow')
.onClick(()=>{
router.pushUrl({url: 'pages/WaterFlowPage'})
})
}
.height('100%')
.width('100%')
}
}
其中DataResource在本示例中是根据Page名、组件名以及索引进行封装,以Page名作为最外层key层,以组件名+索引为里层key值。value值(埋点数据)可以根据实际业务进行配置。
export const DataResource: Record<string, Record<string, DataResourceType>> = {
'Index': {
'text-1': { id: 'text-1' },
'text-2': { id: 'text-2' },
},
'Page2': {
'component-1': {id: 'text-2' },
// ...
}
}
export interface DataResourceType {
id: string
}
点击埋点
配置完埋点数据以及成功绑定组件后,可以在EntryAbility里统一注册点击事件监听,在事件回调中获取点击的触发节点。 UIObserver 一共提供了两种监听事件:
- on(“willClick”):用于监听点击事件指令下发情况,所注册回调将于点击事件触发前触发。
- on(“didClick”):用于监听点击事件指令下发情况,所注册回调将于点击事件触发后触发。
这两种方式均可以实现用户点击组件时触发回调,但要注意必须在组件上添加onClick属性。本示例中以willClick监听为例,下面介绍具体方案实现。
实现步骤
- 首先实现一个简单页面,并且在组件上绑定ID以及埋点数据,ID可以根据组件名-索引来命名;比如下面代码示例中有两个Text组件,ID值分别为“text-1”与“text-2”。
import { DataResource } from '../common/DataResource';
import { router } from '@kit.ArkUI';
@Entry
@Component
struct Index {
onPageShow(): void {
const uiContext: UIContext = this.getUIContext();
console.log('text-1', JSON.stringify(uiContext.getFrameNodeById('text-1')?.getCustomProperty('text-1')));
}
build() {
Column() {
Text('text-1')
.id('text-1')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.customProperty('text-1', DataResource['Index']['text-1'])
.onClick(() => {
console.log('111111');
})
Text('text-2')
.id('text-2')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.customProperty('text-2', DataResource['Index']['text-2'])
.onClick(() => {
})
Button('Navigation')
.onClick(() => {
router.pushUrl({ url: 'pages/NavigationPage' })
})
Button('WaterFlow')
.onClick(()=>{
router.pushUrl({url: 'pages/WaterFlowPage'})
})
}
.height('100%')
.width('100%')
}
}
2.在EntryAbility中统一注册UIObserver的wilClick事件监听,并且在事件回调中获取触发的组件节点FrameNode。
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { FrameNode, window } from '@kit.ArkUI';
import { hiAppEvent } from '@kit.PerformanceAnalysisKit';
import CallbackManager from '../common/CallBackManager';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
console.info('testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
console.info('testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
const uiContext: UIContext = windowStage.getMainWindowSync().getUIContext();
AppStorage.setOrCreate('uiContext', uiContext);
uiContext.getUIObserver()?.on('willClick', (_event: ClickEvent, node?: FrameNode) => {
const clickCallback = CallbackManager.getInstance().getClickCallback();
clickCallback(node, uiContext);
})
uiContext.getUIObserver()
.on('scrollEvent', (info) => CallbackManager.getInstance().getScrollEvent(info))
uiContext.getUIObserver().on('navDestinationSwitc