Skip to content

feat: 手表(wearable)设备类型支持与示例模块 - #53

Open
YoloMao wants to merge 4 commits into
masterfrom
feat/wearable-support
Open

feat: 手表(wearable)设备类型支持与示例模块#53
YoloMao wants to merge 4 commits into
masterfrom
feat/wearable-support

Conversation

@YoloMao

@YoloMao YoloMao commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

让 SDK 支持 HarmonyOS 智能手表(wearable):两个 HAR 声明设备类型、SDK 侧按手表形态调整采集与上报行为、新增手表示例模块。

一、HAR 声明 wearable 设备类型

GrowingAnalyticsGrowingToolsKitdeviceTypes 补入 wearable

module.json5deviceTypes 不可缺省,且各依赖模块都必须包含将要安装的设备类型 —— 此前宿主手表 App 即使自己声明了 wearable,依赖 GrowingAnalytics 时仍会在打包/安装阶段被拦下。

能力核对结论:按 DevEco SDK 内 device-define/ 的 SysCap 定义(HarmonyOS 6.0.0 / API 20)逐项核对,SDK 依赖的 21 项 SysCap 全部在 wearable(270 项)支持列表内,包含 Web.Webview.CoreRelationalStore.CorePreferences.CoreNetManager.Core / NetStackArkUI.ArkUI.FullCryptoFrameworkUtils.Lang 等。手表缺失的能力集中在 AI、Camera、Payment、Stylus、AR 等方向,与数据采集无关。

注意区分 liteWearable(手环)仅 17 项能力,无 ArkUI.Full / RelationalStore / Webview。"鸿蒙手表不支持 WebView"的说法指的是 liteWearable,不在本次支持范围内。

二、SDK 侧手表行为适配

1. 网络状态:新增蓝牙承载类型映射

DeviceInfo.etsnetCapabilitiesChange 回调此前只识别 CELLULAR / WIFI / ETHERNET。手表默认网络优先级为「蓝牙 > WIFI > 蜂窝」,BEARER_BLUETOOTH 无分支时 networkState 会停在上一个值。

现映射为 'WIFI',与既有的 BEARER_ETHERNET 一致 —— 不新增枚举值,服务端、Android / iOS 均无需对齐改动。手机侧的蓝牙网络共享场景同样受益。

2. 上报时机:手表进入后台立即冲刷

AnalyticsCore.writeEventToDisk 中,手表在 APP_CLOSED 写盘后立即触发一次上报(复用 VISIT 的既有机制)。

手表退到后台会被系统快速冻结,仅靠 dataUploadInterval 定时器大概率等不到下一次触发,事件会积压到下次冷启动。该行为deviceType == 'wearable' 生效,手机 / 平板 / PC 的上报节奏不变。与调大上报间隔互补:平时低频省电,退出时保证送达。

3. 设备兜底值按形态区分

新增 DeviceInfo.isWearable,手表将屏幕与设备类型兜底值覆盖为 466 x 466 / 'wearable'

isWearable 仅用于 SDK 内部策略(兜底值、上报时机),不随事件上报,因此不受 IgnoreFields.DeviceType 约束 —— 否则客户开启脱敏后手表的后台冲刷会一并失效。

顺带修正手机兜底值宽高写反(原 height=1260 / width=2720,改为 height=2720 / width=1260),与 orientation 的默认值 PORTRAIT 对齐。这些默认值仅在 display.getDefaultDisplaySync() 取值失败时兜底,且只被圈选与移动调试使用,不进入事件上报字段。

三、新增 entry_wearable 手表示例

新增 entry_wearable 模块(deviceTypes: ["wearable"]),演示在不采集无埋点的前提下接入 SDK。按官方多设备工程结构与 entry 拆开:UI 与交互层无法跨设备复用。

关掉无埋点的正确做法:SDK 有两道独立开关,demo 两道都关。

  1. config.autotrackEnabled = false —— 控制回调内部是否继续处理,但不阻止监听注册
  2. 不调用 GrowingAnalytics.onWindowStageCreate —— 决定性的一步

第 2 点是关键。该方法是 Autotrack.startObserver 的唯一调用方,内部挂 4 个 UIObserver 回调(willClick / navDestinationUpdate / navDestinationSwitch / routerPageUpdate)。仅设 autotrackEnabled = false 而仍然调用它,这 4 个回调依然会注册,每次点击与路由跳转都会进入 SDK 回调再被开关拦下 —— 白白付出唤醒成本。不调用即零注册。

不调用不影响其余功能:手动埋点、会话、设备信息、事件入库与上报由 AnalyticsCore.setLifecycleCallback 内部注册,与 Autotrack 是两条独立链路。

其余手表端配置

配置 原因
hybridAutotrackEnabled false 手表无 WebView 场景
dataUploadInterval 60s(手机端 15s) 减少网络唤醒;后台冲刷保证及时送达
GrowingToolsKit 插件 不挂载 悬浮窗形态,手表屏幕放不下
UI 中心竖列 + 放大点按区域 圆屏四角不放内容,避免误触

没有 PAGE 事件:关闭无埋点后 SDK 不再产生 PAGE 事件,公开 API 中也没有手动页面上报接口(trackFlutterPage 仅供 Flutter 桥接)。本次不为手表端新增 PAGE 接口 —— 手表应用页面层级浅、停留短,分析价值不足以支撑一个新的公开 API。README 只写明这条限制,不给"用自定义事件模拟页面事件"的可抄代码:那种写法产出的是 eventType = Custompath 为空的普通事件,进不了页面分析,作为官方 demo 会被接入方当成推荐 schema 抄走。

四、文档

  • docs/GrowingAnalytics/core/DeviceInfo.md —— 默认值表(手机/手表对照)、初始化流程、网络类型映射表
  • docs/GrowingAnalytics/core/AnalyticsCore.md —— 新增「立即上报的时机」小节
  • entry_wearable/README.md —— 接入说明与手表端取舍

验证

  • assembleHar(两个 HAR)与 assembleHap(entry_wearable)构建通过
  • 产物 module.jsondeviceTypes['default', 'tablet', '2in1', 'wearable'],示例模块为 ['wearable']
  • 声明 wearable 前后各跑一次全量构建做差分:新增 syscap 警告 0、新增 ArkTS 错误 0

已知限制与后续

已知且接受的行为

  • 冷启动首个 VISIT 的 networkStateUNKNOWN:该字段由异步的 netCapabilitiesChange 回调填充,而 VISIT 在 initDeviceInfo() 的同步调用栈中生成,回调赶不上。改为同步查询需两次 IPC,会挤占初始化的主线程预算,权衡后不做。已在 DeviceInfo.md 中说明
  • GrowingToolsKit 虽已声明 wearable,但其悬浮球形态在手表上不可用,示例模块不挂载

本次未覆盖

  • SaaS(MP v2)协议未发送 deviceType,且 VISIT 硬编码 ph: 1、PAGE 硬编码 o: 'portrait',SaaS 侧无法按设备形态拆分数据。本次以 New SaaS 为主,SDK 不做改动:过渡期由报表侧按机型(dm)或分辨率(sh/sw = 466×466)识别手表。彻底方案需在 MP v2 中补设备形态字段并三端对齐口径,另行排期
  • 全仓 canIUse 使用 0 次

待真机验证(模拟器查不到)

  • deviceInfo.deviceType 的实际返回值,以及后端埋点协议是否接受 wearable 枚举
  • 真机蓝牙代理网络的实际 bearerType(手表模拟器上为 BEARER_ETHERNET,属虚拟网卡产物)。若真机并非 BEARER_BLUETOOTH,该分支为 no-op,输出值与 ETHERNET 相同,无副作用
  • 圆形屏幕下 display.getDefaultDisplaySync() 的返回值
  • 手表后台冲刷的实际送达情况、弱网 / 息屏场景下的入库与补发

顺带发现(既有问题,非本 PR 引入):两个 HAR 合计 30 处 syscap 警告全部指向 @hms.collaboration.rcp —— Collaboration.RemoteCommunication 在 default / tablet / 2in1 / phone 上都不在要求能力集内,即上报通道当前已依赖一个"并非所有设备都支持"的 API。建议单开 issue 跟进。

🤖 Generated with Claude Code

两个 HAR 的 deviceTypes 此前只声明了 default/tablet/2in1。module.json5 的
deviceTypes 不可缺省,且各模块都必须包含将要安装的设备类型,因此宿主手表 App
即使自己声明了 wearable,依赖 GrowingAnalytics 时仍会在打包/安装阶段被拦下。

按 DevEco SDK 内 device-define/ 的权威 SysCap 定义核对(HarmonyOS 6.0.0/API 20):
wearable 支持 270 项能力,SDK 依赖的 21 项(Web.Webview.Core、
RelationalStore.Core、Preferences.Core、NetManager.Core/NetStack、ArkUI.Full、
CryptoFramework、Utils.Lang 等)全部在列,手表缺失的 182 项集中在 AI、Camera、
Payment、Stylus 等方向,与数据采集无关。

两个模块各跑了 baseline / +wearable 的全量构建做差分,新增 syscap 警告 0 条、
新增 ArkTS 错误 0 条,deviceTypes 正确落入 HAR 产物。

注意:liteWearable(手环,仅 17 项能力,无 ArkUI.Full / RelationalStore /
Webview)不在本次支持范围内。示例应用 entry 未加 wearable——按官方多设备工程
结构,手表应另开独立 entry module。运行时行为(无埋点 willClick/FrameNode、
deviceType 枚举上报、屏幕尺寸兜底)仍需真机验证。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@YoloMao
YoloMao force-pushed the feat/wearable-support branch from e096de4 to fcf07d8 Compare August 17, 2026 06:31
新增手表端示例模块 entry_wearable(deviceTypes: ["wearable"]),演示在不采集
无埋点的前提下接入 GrowingAnalytics。按官方多设备工程结构与 entry 拆开:UI 与
交互层无法跨设备复用,手机端仍由 entry 承载。

关闭无埋点的正确做法是两道开关都关:

1. config.autotrackEnabled = false(默认值,此处显式写出表明有意为之)
2. 不调用 GrowingAnalytics.onWindowStageCreate —— 这才是决定性的一步

第 2 点是关键。该方法是无埋点 UI 监听的唯一注册入口(Autotrack.startObserver
的唯一调用方),内部挂 4 个 UIObserver 回调:willClick、navDestinationUpdate、
navDestinationSwitch、routerPageUpdate。仅设 autotrackEnabled = false 而仍调用
它,回调依然注册,每次点击与路由跳转都会进入 SDK 回调再被开关拦下。手表 CPU 与
续航更紧张,不调用即零注册。

不调用不影响其余功能:手动埋点、会话、设备信息、事件入库与上报由
AnalyticsCore.setLifecycleCallback 内部注册,与 Autotrack 是两条独立链路。

其余手表端适配:hybridAutotrackEnabled 关闭(无 WebView 场景)、
dataUploadInterval 放宽到 60s 减少网络唤醒、不挂载 GrowingToolsKit(悬浮窗形态
手表放不下)、圆屏 UI 内容集中在中心竖列并放大点按区域。

关闭无埋点后 SDK 不再产生 PAGE 事件,公开 API 中也没有手动页面上报接口。手表端
不为此新增 PAGE 接口,demo 也不演示"用自定义事件模拟页面事件"的写法:那只是
track(name, attrs) 的一次普通调用,不演示任何新 API,却容易被当成推荐 schema
抄走——自定义事件的 eventType 是 Custom,属性落在 attributes map 里,与
PageEvent 的顶层 path / title / timestamp 无关,进不了页面分析。README 只保留
这条限制说明,不给可抄的代码。

验证:assembleHap 全量构建通过(0 error、0 syscap 警告),产物 module.json 的
deviceTypes 为 ["wearable"]。运行时行为(deviceType 实际取值、圆屏 display
返回值)仍需真机确认。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@YoloMao
YoloMao force-pushed the feat/wearable-support branch from fcf07d8 to 658e19e Compare August 17, 2026 07:06
YoloMao and others added 2 commits August 17, 2026 15:26
…ments

删除手动页面事件后遗留的两处过时措辞,以及从 entry 抄来但在手表模块无意义的配置。

过时注释(与"不产生 PAGE 事件、也不演示替代写法"的实际行为矛盾,会让读者去找
一个不存在的手动上报入口):

- Index.ets 文件注释「页面浏览事件需要手动补」→ 改为说明不产生 PAGE/VIEW_CLICK,
  事件全部来自手动埋点 API
- README 对比表「PAGE 事件 | 需手动补(见下)」→ 改为「不产生,且不新增手动接口」

死代码与冗余配置:

- AppStorage.setOrCreate('sdkMode', config.mode):唯一消费者是 entry 的
  pages/Hybrid.ets 里的 @StorageProp('sdkMode'),而手表模块没有 Hybrid 页面
  (无 WebView 场景本就是本 demo 的设定),写入后无人读取
- config.autotrackAllPages = false:三重冗余——GrowingConfig 默认即为 false;
  该配置仅影响 NativePage;而 AutotrackPage 的 observer 因不调用
  onWindowStageCreate 根本没注册。相邻的 autotrackEnabled = false 有注释交代
  "显式写出以表明有意为之",此项没有同等理由,只是把 entry 的 true 机械翻转
- onAcceptWant:module.json5 未声明 launchType(默认 singleton),该回调仅在
  specified 模式下触发,属 DevEco 模板样板;连带移除随之失效的 Want 导入

验证:assembleHap 全量构建通过(exit 0、0 error、0 syscap 警告)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- DeviceInfo: 新增 BEARER_BLUETOOTH 映射(归为 WIFI,与 ETHERNET 一致)。
  手表默认网络优先级为「蓝牙 > WIFI > 蜂窝」,此前该承载类型无分支,
  networkState 会停在上一个值。
- DeviceInfo: 新增 isWearable,手表覆盖屏幕与设备类型兜底值为 466x466 /
  'wearable'。isWearable 仅用于 SDK 内部策略,不随事件上报,故不受
  IgnoreFields.DeviceType 约束。
- DeviceInfo: 修正手机屏幕兜底值宽高写反(原 1260x2720,应为宽 1260 高 2720),
  与 orientation 默认值 PORTRAIT 对齐。
- AnalyticsCore: 手表在 APP_CLOSED 写盘后立即冲刷上报。手表退到后台会被系统
  快速冻结,仅靠定时器大概率等不到,事件会积压到下次冷启动。手机/平板/PC
  行为不变。

冷启动首个 VISIT 的 networkState 为 UNKNOWN 属已知且接受的行为(异步回调赶不上
同步生成的 VISIT,同步查询需两次 IPC,会挤占初始化主线程预算),已在文档中说明。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant