8.6 KiB
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。
优化方案
- 注册
addAboutToDisappearCallBack(),页面销毁时统一调用stopRequest()。 stopRequest()在取消三个请求后清空每个 client 的 bridge 引用和OverViewEvent成员引用,使重复调用保持幂等。- 销毁回调最后清空
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(realDataRequest,mobipush 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():
UnifiedSocketRequestEvent.onDestroy():逐 clientremoveRequest()(清全局队列 + 缓冲)→ 清requestClients/requestParamModels→ 置空webViewJSBridge。MobiPushRealDataRequestEvent.onDestroy():逐RealTimeDataRequestClient.removeRequest()(总数据removeByClient+ 增量unsub退订并removeByClient)→ 清数组/callBackMap→ 置空 bridge。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 页面销毁时统一触发释放。
验证结果
-
对实际
OverViewEvent.ets转译后使用模拟 bridge、请求缓冲和队列执行 20,000 轮“注册 → 发起 3 个请求 → 页面销毁”;每轮销毁后 retained request 为 0、retained client 为 0。 -
使用直接 Hvigor 构建
entry@defaultDebug HAP 成功,完整 ArkTS 编译、HAP 打包和签名通过;未执行安装。 -
临时压力测试文件和日志已删除,生产代码未保留计数、测试入口或调试日志。
-
本机缺少项目原始 API 20 SDK,构建时临时使用 API 24 并已恢复配置;未执行模拟器页面操作、设备堆快照或 OOM 日志复测。
-
2026-08-21 在 Pura 90 API 24 模拟器安装并启动修复包,确认“行情 → 总览”页面可加载;主 Tab 切换只改变前后台状态,不销毁
OverViewPage,当前产品入口无法在不终止 Ability 的情况下重复触发本修复的页面销毁回调,因此未将主 Tab 循环冒充为OverViewEvent销毁验证。 -
同次设备测试检查应用 Crash 日志及近 20 分钟错误日志,未发现 OOM、JS Crash、CppCrash、SIGSEGV 或 SIGABRT。
-
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常驻HQPageSwiper,页内 Tab 切换不销毁、无法用 UI 触发其销毁回调(同第 5 点);销毁时在途请求 > 0 的完整释放由代码静态确认(removeRequest→removeByClient双向移除)与既有 2 万轮模拟覆盖。
-
2026-08-25 真机压力测试(nova 14 Pro,MIA-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 行口径)。