docs: update WebView lifecycle remediation

This commit is contained in:
clz
2026-08-25 19:14:08 +08:00
parent 6cfad5fa82
commit 380b0c3a6a
3 changed files with 52 additions and 6 deletions
@@ -5,6 +5,7 @@
| 仓库 | 分支 | Commit ID | 说明 |
| --- | --- | --- | --- |
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `da5599387d82f06256e9224eb8c8001410de1a6b` | 补齐总览页面销毁解绑并断开 bridge 引用 |
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `27a582034` | 扩展:其他三个 WebView 请求事件的销毁释放 |
## 问题
@@ -16,6 +17,35 @@
2. `stopRequest()` 在取消三个请求后清空每个 client 的 bridge 引用和 `OverViewEvent` 成员引用,使重复调用保持幂等。
3. 销毁回调最后清空 `OverViewEvent.webViewJSBridge`,切断事件对象到 WebView bridge 的引用。
## 扩展修复(2026-08-25):其他 WebView 请求事件的销毁释放
原修复只覆盖 `OverViewEvent` 的三条行情请求。审计 `WebServiceProvider` 注册的全部请求类 JS 事件后,发现还有三个事件存在同类「全局队列持有 client → client 回调闭包持有事件 → 事件持有 bridge」的跨页 retained reference,本次一并补齐销毁释放。
### 排查结论
| 事件 | client 登记方式 | 销毁时清理 | 结论 |
| --- | --- | --- | --- |
| `UnifiedSocketRequestEvent`UnifiedRequestBridge,所有 WebView 通用) | `TcpRequestClient.request()``base(frameId,pageId,client,…)` 登记全局队列 | 无(仅 onBackground | 泄漏 |
| `MobiPushRealDataRequestEvent`realDataRequestmobipush 6004/6005 | `TotalDataRequestClient`/`IncrementDataRequestClient``base(…,client,…)` 登记全局队列 | 无(仅 onBackground | 泄漏 |
| `ForwardEvent`QHTForward,账户分析透传 1101 | `ForwardTool` 显式 `getInstanceId(this)` 登记 | 无 | 较轻:不反向持有 bridge,但销毁时未响应的请求残留队列 |
| `GetHQEvent` | `requestHq()` 登记 | `WebViewComponent.aboutToDisappear` 显式调 `GetHQEvent.release()` | 已处理,无需扩展 |
队列登记机制依据:`lib_communication` 编译产物 `AbsRequestBuilder.base()` 第三个参数为 client 时内部调用 `getInstanceId(client)` 统一登记,`QueueManagement.removeByClient(client)``queue``primaryClientById` 双向移除,`RealTimeDataRequestClient.removeRequest()` 额外对增量通道发 `unsub` 真退订。
### 优化方案
`OverViewEvent` 同构,在 `registerJSEvent` 注册 `addAboutToDisappearCallBack()`,销毁时统一调用各自 `onDestroy()`
1. `UnifiedSocketRequestEvent.onDestroy()`:逐 client `removeRequest()`(清全局队列 + 缓冲)→ 清 `requestClients`/`requestParamModels` → 置空 `webViewJSBridge`
2. `MobiPushRealDataRequestEvent.onDestroy()`:逐 `RealTimeDataRequestClient.removeRequest()`(总数据 `removeByClient` + 增量 `unsub` 退订并 `removeByClient`)→ 清数组/`callBackMap` → 置空 bridge。
3. `ForwardEvent.onDestroy()`:逐 `ForwardTool``removeByClient` → 清空 `toolArray`
幂等性:`removeByClient`/`removeRequest` 对未登记项安全返回;销毁回调在 bridge 生命周期内只执行一次。
### 验证日志
设备验证期间曾在销毁清理路径临时添加 `HXLog.i` 验证日志(`WebViewJavaScriptBridge.aboutToDisappear` 起始、各事件 `onDestroy` 移除数量、`OverViewEvent` 销毁、`GetHQEvent.release`),用于确认销毁清理实际生效;验证完成后已全部移除,生产代码未保留调试日志。
## 影响范围
- 仅修改行情总览 JS 事件的销毁生命周期。
@@ -26,12 +56,28 @@
每次总览页销毁会确定释放 3 个请求 client、对应请求缓冲和队列映射,避免其随反复进出页面线性累积。实际节省内存取决于 bridge、请求参数和页面对象引用图,本次没有设备堆快照,因此不估算具体 MB 或百分比。
该修复只覆盖 `OverViewEvent` 的三条行情请求,不代表其他 WebView JS 事件或全局行情链路已全部完成生命周期审计。
该修复只覆盖 `OverViewEvent` 的三条行情请求,不代表其他 WebView JS 事件或全局行情链路已全部完成生命周期审计。2026-08-25 扩展修复后,WebView 层持有全局请求 client 的事件已全部补齐销毁释放;全局行情链路仍有审计空间。)
扩展修复仅新增销毁生命周期清理,前台/后台回调、请求解析、`receive` 回调与协议参数均未改动;三个事件为 `WebServiceProvider` 全局注册,所有 WebView 页面销毁时统一触发释放。
## 验证结果
1. 对实际 `OverViewEvent.ets` 转译后使用模拟 bridge、请求缓冲和队列执行 20,000 轮“注册 → 发起 3 个请求 → 页面销毁”;每轮销毁后 retained request 为 0、retained client 为 0。
2. 使用直接 Hvigor 构建 `entry@default` Debug HAP 成功,完整 ArkTS 编译、HAP 打包和签名通过;未执行安装。
3. 临时压力测试文件和日志已删除,生产代码未保留计数、测试入口或调试日志。
4. 确认“行情 → 总览”页面可加载;主 Tab 切换只改变前后台状态,不销毁 `OverViewPage`,当前产品入口无法在不终止 Ability 的情况下重复触发本修复的页面销毁回调,因此未将主 Tab 循环冒充为 `OverViewEvent` 销毁验证
5. 同次设备测试检查应用 Crash 日志及近 20 分钟错误日志,未发现 OOM、JS Crash、CppCrash、SIGSEGV 或 SIGABRT
4. 本机缺少项目原始 API 20 SDK,构建时临时使用 API 24 并已恢复配置;未执行模拟器页面操作、设备堆快照或 OOM 日志复测
5. 2026-08-21 在 Pura 90 API 24 模拟器安装并启动修复包,确认“行情 → 总览”页面可加载;主 Tab 切换只改变前后台状态,不销毁 `OverViewPage`,当前产品入口无法在不终止 Ability 的情况下重复触发本修复的页面销毁回调,因此未将主 Tab 循环冒充为 `OverViewEvent` 销毁验证
6. 同次设备测试检查应用 Crash 日志及近 20 分钟错误日志,未发现 OOM、JS Crash、CppCrash、SIGSEGV 或 SIGABRT。
7. **2026-08-25 扩展修复设备验证(Pura 90 模拟器,API 24**
- `./claude_tool/claude_compile.sh` 编译 `BUILD_SUCCESS`,重装启动正常,无 OOM/JS Crash/CppCrash。
- 销毁链验证:打开热点资讯文章 WebView(推入)→ 返回键销毁,hilog 显示 `WebViewJavaScriptBridge: aboutToDisappear start, disappearCallbacks=4``ForwardEvent`/`UnifiedRequestBridge`/`MobiPushRealDataRequest` 依次 `onDestroy`(移除 client 数量 0,文章页未发起该类请求)→ `OverViewEvent: destroy: stopRequest release bridge``GetHQEvent.release`。四个销毁回调 + GetHQ 显式释放全部执行,无异常。
- OverViewEvent 回归:行情 → 总览页三个数据块(指数行情表格、品种资金流向·亿、主力持仓多空比)渲染正常,实时推送持续更新(`0.0320/0.03%``0.0480/0.05%`);Home 后台仅 `onBackground`/`onHide` 不销毁;回前台 `onShow` 后请求重建、数据继续更新,前后台恢复行为保持不变。
- 边界:行情总览 `OverViewPage` 常驻 `HQPage` Swiper,页内 Tab 切换不销毁、无法用 UI 触发其销毁回调(同第 5 点);销毁时在途请求 > 0 的完整释放由代码静态确认(`removeRequest``removeByClient` 双向移除)与既有 2 万轮模拟覆盖。
8. **2026-08-25 真机压力测试(nova 14 ProMIA-AL00,真机)**:与模拟器分别验证,重点确认修复包在真机上的完整销毁链与反复进出无内存累积。
- 覆盖安装修复 HAP 至真机(覆盖真机上的修复前旧包),启动后反复进入/断开可销毁的 WebView 页面。
- 完整销毁链与模拟器一致:`WebViewJavaScriptBridge: aboutToDisappear start, disappearCallbacks=4``ForwardEvent`/`UnifiedRequestBridge`/`MobiPushRealDataRequest` 依次 `onDestroy``OverViewEvent: destroy``GetHQEvent.release, hqEventInstanceMap size=1`,四销毁回调 + GetHQ 显式释放全部执行。
- 稳定锚点:`期货学院`首页入口 `https://fupage.10jqka.com.cn/education/index.html` 为独立可推入/销毁的 WebView 页,作为往复钻压测目标;行情自选详情页为原生页(仅 `QuoteDetailPage aboutToDisappear`,无 WebView bridge)。
- 连续 15 轮进出该 WebView(进→返回销毁):app 主进程 PID 全程存活,VmRSS 稳定在 ~749–762 MB 窄幅波动(无逐轮累积),VmHWM ~792 MB,未触发 OOM/JS Crash/CppCrash/SIGSEGV/SIGABRT。
- 压测所用的 `onDestroy` 验证日志为临时追加,压测完成后已全部移除,生产代码未保留调试日志(同第 3、47 行口径)。