Files

84 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WebView 行情总览销毁时释放请求 Client
## 修复提交
| 仓库 | 分支 | 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 请求事件的销毁释放 |
## 问题
`OverViewEvent` 只处理 WebView 前后台切换,没有注册页面销毁回调。三个行情 client 已登记到全局 `RequestQueueManagement` 并写入请求缓冲后,如果 WebView 直接销毁,全局队列仍可能持有 client,client 又反向持有 `WebViewJavaScriptBridge`,形成跨页面 retained reference。
## 优化方案
1. 注册 `addAboutToDisappearCallBack()`,页面销毁时统一调用 `stopRequest()`
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 事件的销毁生命周期。
- 不修改三个行情协议的 frameId、pageId、请求文本、解析或回调数据结构。
- 前后台恢复行为保持不变;已收到 JS 事件时仍会在回到前台后重新创建请求。
## 预期收益与边界
每次总览页销毁会确定释放 3 个请求 client、对应请求缓冲和队列映射,避免其随反复进出页面线性累积。实际节省内存取决于 bridge、请求参数和页面对象引用图,本次没有设备堆快照,因此不估算具体 MB 或百分比。
该修复只覆盖 `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. 本机缺少项目原始 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 行口径)。