一、埋点

   埋点是指将信息采集程序和原本的功能代码结合起来,针对特定用户行为收集、处理和发送一些信息,用来跟踪应用使用情况。包括访问数、访客数、停留时长、页面浏览数和跳出率。

场景 说明
点击统计 统计组件点击频率,分析用户偏好
滑动监听 计算滑动偏移量和曝光比例
页面切换 统计停留时间、来源页和目标页
性能分析 计算加载各节点耗时,针对性优化

二、埋点分类

埋点类型 行为属性 说明
点击埋点 主动行为 用户在任意区域的一次单击(icon、图片等)
曝光埋点 被动行为 统计页面局部区域是否被有效浏览(瀑布流卡片曝光比例和时长)
页面埋点 - 统计停留时间、加载性能、跳转来源/去向

三、整体方案

整体方案使用全局无感监听能力实现埋点功能:

  • UIObserver:全局监听点击事件、滑动事件、页面切换

  • setOnVisibleAreaApproximateChange:监听组件可视区域变化(曝光埋点)

3.1 UIObserver提供的事件

事件 说明
on('willClick') 点击事件指令下发前触发
on('didClick') 点击事件指令下发后触发
on('scrollEvent') 组件滑动开始/结束回调
on('navDestinationSwitch') Navigation页面切换监听
on('routerPageUpdate') Router页面切换监听
on('willDraw') 首帧绘制前
on('didLayout') 完成绘制时

四、绑定埋点数据

4.1 组件绑定ID和埋点数据

Button('Click Tracing Point - Single Component')
  .width('100%')
  .id('button-1')
  .fontWeight(FontWeight.Bold)
  .customProperty('button-1', DataResource['Index']['button-1'])
  .onClick(() => {
    hilog.info(0x0000, 'ApplicationTrack', 'btn');
  })

4.2 封装埋点数据资源

// DataResource.ets
export const DataResource: Record<string, Record<string, DataResourceType>> = {
  'Index': {
    'button-1': { id: 'button-1' },
    'button-2': { id: 'button-2' }
  },
  'Page2': {
    'component-1': { id: 'text-2' }
  }
}

export interface DataResourceType {
  id: string
}

五、点击埋点实现

5.1 在EntryAbility中注册点击监听

// EntryAbility.ets
uiContext.getUIObserver()?.on('willClick', (_event: ClickEvent, node?: FrameNode) => {
  const clickCallback = CallbackManager.getInstance().getClickCallback();
  clickCallback(node, uiContext);
})

5.2 处理点击回调

// CallbackManager.ets
public getClickCallback() {
  return (node: FrameNode | undefined, uiContext: UIContext) => {
    const uniqueId = node?.getUniqueId();
    const ID = node?.getId();
    const pageInfo = uiContext.getPageInfoByUniqueId(uniqueId);
    const trackData = node?.getCustomProperty(ID);
    
    let eventParams: Record<string, string | number> = {
      'component_id': ID ?? '',
      'pageInfo': JSON.stringify(pageInfo ?? {}),
      'trackData': JSON.stringify(trackData ?? {})
    };
    
    hiAppEvent.write({
      domain: 'test_domain',
      name: 'test_event',
      eventType: hiAppEvent.EventType.FAULT,
      params: eventParams
    }, (err: BusinessError) => {
      // 处理回调
    });
  };
}

5.3 取消监听

// onWindowStageDestroy
uiContext?.getUIObserver().off('willClick');

六、曝光埋点实现

6.1 自定义TrackNode钩子组件

TrackNode组件需支持:

  1. 嵌套子组件

  2. 组件ID值注入

  3. 注册监听事件

// TrackNode.ets
@Builder
function TrackNode(
  { track }: { track?: Track },
  content: () => void
) {
  TrackNodeComponent({ track: track }, content)
}

@Component
struct TrackNodeComponent {
  @ObjectLink track: Track;
  
  onDidBuild() {
    let uid = this.getUniqueId();
    let node: FrameNode | null = this.getUIContext().getFrameNodeByUniqueId(uid);
    
    // 1. 将组件与TrackShadow对象绑定
    TrackManager.get().addTrack(this.trackShadow.id, this.trackShadow);
    
    // 2. 监听可视区域变化(设置0、0.5、1比率,500ms更新间隔)
    node?.commonEvent.setOnVisibleAreaApproximateChange(
      { ratios: [0, 0.5, 1], expectedUpdateInterval: 500 },
      (ratioInc: boolean, ratio: number) => {
        this.trackShadow.visibleRatio = ratio;
      }
    );
    
    // 3. 向上追溯父节点,建立父子关系
    // ...
  }
  
  aboutToDisappear() {
    TrackManager.get().removeTrack(this.trackShadow.id);
  }
  
  build() {
    // 包裹子组件
    content();
  }
}

6.2 TrackManager曝光数据管理

export class TrackManager {
  private trackMap: Map<string, TrackShadow> = new Map();
  private rootTrack: TrackShadow | null = null;
  
  addTrack(id: string, track: TrackShadow): void {
    this.trackMap.set(id, track);
  }
  
  removeTrack(id: string): void {
    this.trackMap.delete(id);
  }
  
  getTrackById(id: string): TrackShadow | undefined {
    return this.trackMap.get(id);
  }
  
  dump(): void {
    this.rootTrack?.dump(0);  // 递归输出所有子组件的曝光比例
  }
}

6.3 使用TrackNode包裹组件

// 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}`)
        }
      }
    })
  }
}

七、页面埋点实现

7.1 Navigation路由监听

// EntryAbility.ets
uiContext.getUIObserver().on('navDestinationSwitch', (info) => {
  const switchCallback = CallbackManager.getInstance().getSwitchCallback();
  switchCallback(info);
})

回调参数

字段 说明
context 页面上下文信息
from 来源页
to 去向页
operation 页面操作类型

7.2 Router路由监听

// EntryAbility.ets
uiContext.getUIObserver().on('routerPageUpdate', (info) => {
  const switchCallback = CallbackManager.getInstance().getSwitchCallback();
  switchCallback(info);
})

回调参数

字段 说明
context 页面上下文信息
index 触发页面在路由栈中的位置
name 触发页面名称
path 触发页面路径
state 页面状态(ABOUT_TO_APPEAR/ON_PAGE_HIDE/ON_PAGE_SHOW)
pageId 页面唯一标识

7.3 页面加载性能监听

aboutToAppear(): void {
  const uiContext = this.getUIContext();
  
  uiContext.getUIObserver().on('willDraw', () => {
    this.startTime = Date.now();  // 首帧开始
  })
  
  uiContext.getUIObserver().on('didLayout', () => {
    this.endTime = Date.now();    // 绘制完成
  })
}

八、埋点数据上传

通过hiAppEvent.addWatcher()添加订阅事件观察者,在onTrigger回调中实现数据上报。

const onTrigger = CallbackManager.getInstance().getOnTrigger();

hiAppEvent.addWatcher({
  name: 'watcher1',
  appEventFilters: [
    {
      domain: 'test_domain',
      eventTypes: [hiAppEvent.EventType.FAULT, hiAppEvent.EventType.BEHAVIOR]
    }
  ],
  triggerCondition: {
    row: 10,      // 10条事件
    size: 1000,   // 或1000字节
    timeOut: 1    // 或1秒
  },
  onTrigger: onTrigger
})

九、三种埋点对比

埋点类型 实现方式 核心API
点击埋点 UIObserver点击监听 + hiAppEvent.write on('willClick') + getCustomProperty
曝光埋点 setOnVisibleAreaApproximateChange监听 + TrackNode封装 setOnVisibleAreaApproximateChange + 自定义TrackManager
页面埋点 UIObserver页面切换监听 on('navDestinationSwitch') / on('routerPageUpdate')

数据上报流程

埋点数据写入(hiAppEvent.write)
    ↓
本地事件文件存储
    ↓
addWatcher订阅(设置触发条件)
    ↓
onTrigger触发
    ↓
HTTP请求上报服务器

注意事项

  1. hiAppEvent.write参数:params的值只能是number、string、boolean及数组类型

  2. 曝光埋点限制:组件未发生变化时不触发回调(如Item达到500ms后用户不再滑动,不会被记录)

  3. scrollEvent限制:id值仅能精确到外层组件,无法精确到内层子组件(曝光比例需用setOnVisibleAreaApproximateChange)

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐