Files
future-harmony-freeze-oom-fix/harmony-ths-futures-apm-oom/doc/change-notes/13-webview-overview-request-cleanup.md
T

8.6 KiB
Raw Blame History

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 登记方式 销毁时清理 结论
UnifiedSocketRequestEventUnifiedRequestBridge,所有 WebView 通用) TcpRequestClient.request()base(frameId,pageId,client,…) 登记全局队列 无(仅 onBackground 泄漏
MobiPushRealDataRequestEventrealDataRequestmobipush 6004/6005 TotalDataRequestClient/IncrementDataRequestClientbase(…,client,…) 登记全局队列 无(仅 onBackground 泄漏
ForwardEventQHTForward,账户分析透传 1101 ForwardTool 显式 getInstanceId(this) 登记 较轻:不反向持有 bridge,但销毁时未响应的请求残留队列
GetHQEvent requestHq() 登记 WebViewComponent.aboutToDisappear 显式调 GetHQEvent.release() 已处理,无需扩展

队列登记机制依据:lib_communication 编译产物 AbsRequestBuilder.base() 第三个参数为 client 时内部调用 getInstanceId(client) 统一登记,QueueManagement.removeByClient(client)queueprimaryClientById 双向移除,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():逐 ForwardToolremoveByClient → 清空 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=4ForwardEvent/UnifiedRequestBridge/MobiPushRealDataRequest 依次 onDestroy(移除 client 数量 0,文章页未发起该类请求)→ OverViewEvent: destroy: stopRequest release bridgeGetHQEvent.release。四个销毁回调 + GetHQ 显式释放全部执行,无异常。
    • OverViewEvent 回归:行情 → 总览页三个数据块(指数行情表格、品种资金流向·亿、主力持仓多空比)渲染正常,实时推送持续更新(0.0320/0.03%0.0480/0.05%);Home 后台仅 onBackground/onHide 不销毁;回前台 onShow 后请求重建、数据继续更新,前后台恢复行为保持不变。
    • 边界:行情总览 OverViewPage 常驻 HQPage Swiper,页内 Tab 切换不销毁、无法用 UI 触发其销毁回调(同第 5 点);销毁时在途请求 > 0 的完整释放由代码静态确认(removeRequestremoveByClient 双向移除)与既有 2 万轮模拟覆盖。
  8. 2026-08-25 真机压力测试(nova 14 ProMIA-AL00,真机):与模拟器分别验证,重点确认修复包在真机上的完整销毁链与反复进出无内存累积。

    • 覆盖安装修复 HAP 至真机(覆盖真机上的修复前旧包),启动后反复进入/断开可销毁的 WebView 页面。
    • 完整销毁链与模拟器一致:WebViewJavaScriptBridge: aboutToDisappear start, disappearCallbacks=4ForwardEvent/UnifiedRequestBridge/MobiPushRealDataRequest 依次 onDestroyOverViewEvent: destroyGetHQEvent.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 行口径)。