docs: add stability remediation evidence
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Scope
|
||||
|
||||
This repository is an evidence bundle for HarmonyOS futures-app freeze and OOM remediation; it does not contain buildable application source code.
|
||||
|
||||
- `harmony-ths-futures-freeze/doc/change-notes/` contains freeze/performance remediation notes, numbered `01`–`11`.
|
||||
- `harmony-ths-futures-apm-oom/doc/change-notes/` contains memory-retention remediation notes, numbered `10`–`15`.
|
||||
- `change-notes.zip` is the distributable archive of those two directories. Keep it synchronized only when a deliverable archive is requested.
|
||||
|
||||
Treat notes as technical records: distinguish changes made in the app, communication HAR, and trade SDK repositories, and do not imply that an upstream change is present here.
|
||||
|
||||
## Documentation Style & Naming
|
||||
|
||||
Write Markdown in Chinese, matching the existing material. Use direct `#` and `##` headings and concise paragraphs. New notes should follow the established numbered, kebab-case pattern, for example `12-new-memory-audit.md`.
|
||||
|
||||
Use fenced code blocks for commands and backticks for paths, module names, APIs, package versions, branches, and commit IDs. Preserve exact measurements, units, dates, device details, and command output; identify estimates and limitations explicitly. Prefer tables for baseline-versus-result comparisons.
|
||||
|
||||
## Validation and Development Commands
|
||||
|
||||
There is no local build, formatter, linter, or test suite. Do not run application builds from this repository. Record validation performed in the relevant upstream HarmonyOS checkout, for example:
|
||||
|
||||
```sh
|
||||
devecocli build --modules entry@default
|
||||
devecocli check lint
|
||||
devecocli log --crash --bundle-name <bundle-id>
|
||||
```
|
||||
|
||||
State whether each command was actually run, its target (module/device), and any skipped coverage. Do not present a suggested command as a completed verification.
|
||||
|
||||
## Change Notes and Review
|
||||
|
||||
Each remediation note should include: the fix commit(s) and branch(es), problem statement, changed scope, implementation/mitigation, behavior and risk boundaries, and concrete verification results. Keep unrelated cleanup and temporary test artifacts out of documented production changes.
|
||||
|
||||
This bundle has no Git metadata, so no local commit-message convention can be inferred. Use a short imperative subject such as `docs: add table request cleanup note`. Pull requests should name the affected upstream repository and commit, link the incident or ticket when available, and include before/after metrics or screenshots/log excerpts for behavior claims.
|
||||
Binary file not shown.
+44
@@ -0,0 +1,44 @@
|
||||
# 行情表格请求切换的订阅与缓冲清理
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `5f6e007c7f431521350a68543c4db5314a8c644f` | 修复 `TableRequestClient` 订阅泄漏 |
|
||||
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `2ca4759c4e0212b4c80c41b94a0ae1339569e89c` | 完善请求切换时的统一清理逻辑 |
|
||||
|
||||
## 问题
|
||||
|
||||
行情列表通过 `TableRequestClient` 发起请求,并在带 `newrealtime=1` 的请求中向 `RealDataManager` 注册实时订阅。请求队列映射、实时订阅和请求缓冲分别以 instanceId、instanceId、`frameId + pageId + instanceId` 关联。
|
||||
|
||||
原有 `setPageId()` 先覆盖成员 `pageId`,再移除请求队列映射;随后无法获得旧 instanceId,也没有同步清理旧实时订阅与旧请求缓冲。切换行情分组时,`RealDataManager` 可能持续持有旧表格订阅并合并推送数据,造成 Local Heap 持续增长,最终有 OOM 风险。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. `setPageId()` 在覆盖成员前保存旧 `pageId`,递增请求生命周期版本并取消待执行通知,再统一解绑旧请求。
|
||||
2. 新增私有 `detachCurrentRequest(oldPageId)`:调用 `RequestQueueManagement.removeByClient(this)` 原子取得并移除旧 instanceId 映射;当旧 ID 有效时,用该 ID 移除 `RealDataManager` 订阅,并以旧 `pageId` 删除对应请求缓冲。
|
||||
3. `removeSubscriber()` 与 `release()` 复用同一解绑逻辑,避免多个生命周期出口清理策略不一致。
|
||||
4. 保留既有异步版本校验和 `setTimeout` 取消逻辑,防止过期解析结果或通知闭包继续持有表格数据。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅修改 `lib_baseUI` 的 `TableRequestClient` 请求切换和释放生命周期。
|
||||
- 不修改协议报文、请求参数、表格解析、实时数据合并或 UI 展示逻辑。
|
||||
- 请求未建立队列映射时,清理操作保持幂等并直接返回。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
每次行情分组切换和页面销毁都会同时释放旧请求的队列映射、实时订阅及请求缓冲,避免孤儿订阅继续持有全量表格并接收实时推送,从而抑制 Local Heap 随切页线性增长。
|
||||
|
||||
该修改只治理 `TableRequestClient` 链路的引用滞留;图形内存、Web、Native/CPP 堆及其他业务模块的内存增长仍需按各自链路继续分析。`removeSubscriber` 与缓冲删除均按旧 instanceId 执行,因此不会误删新请求。
|
||||
|
||||
## 验证建议
|
||||
|
||||
1. 执行 `devecocli build --modules entry@default`,确认 ArkTS 编译和打包通过。
|
||||
2. 安装新 HAP 后,在“行情 → 国内”页的“全部主力 / 能源”等分组间连续切换 20~50 次,再离开列表页。
|
||||
3. 使用临时调试计数验证每次切换后旧订阅和旧请求缓冲各减少一项,待执行通知数归零;验证完成后不保留计数日志或缓冲快照统计代码。
|
||||
4. 检查应用崩溃日志,并结合 APM/HiLog 观察 Local Heap 在循环切换期间不再线性增长。
|
||||
|
||||
## 本次验证结果
|
||||
|
||||
在模拟器上完成 20 次同一活跃 `TableRequestClient` 的分组切换:每轮旧订阅数由 2 降至 1、请求缓冲数由 4 降至 3,待执行通知数始终为 0;离开行情列表后订阅数由 1 降至 0。未发现该应用的崩溃日志。上述计数日志仅用于本次验收,已从正式代码移除。
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# 行情表格实时通知合并
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `909ab7e83795ca418138da81dc8273c29046ce55` | 合并同一事件循环内的行情表格实时通知 |
|
||||
|
||||
## 问题
|
||||
|
||||
`TableRequestClient.notifyDataReceive()` 原先会为每条行情通知创建一个 `setTimeout` 回调,并将对应的 `TableData` 闭包保存在待执行回调中。实时推送频繁、UI 线程暂时繁忙时,同一轮事件循环内可能堆积多个回调,每个回调都引用一份完整表格数据,放大瞬时 Local Heap 占用。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. 将待通知状态改为单个定时器 ID 和最新一份 `TableData | string`。
|
||||
2. 新数据到达时仅覆盖待发送数据;已有定时器时不再创建新的回调。
|
||||
3. 定时器执行时读取并清空最新数据,再按既有请求生命周期版本校验后通知 UI。
|
||||
4. 请求切换和释放时取消该定时器并清空待通知数据,避免异步回调继续引用旧表格。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅修改 `lib_baseUI` 中 `TableRequestClient` 的通知调度方式。
|
||||
- 表格解析、实时订阅、请求参数和 UI 回调接口均不变。
|
||||
- 同一事件循环内 UI 只接收最新一份表格数据;中间的过期快照不再逐份分发。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
待执行通知从“每条推送一个闭包”收敛为“一个定时器加一份最新数据”,降低高频行情期间短时间内同时存活的完整表格数量,并避免页面切换后遗留的待通知数据。
|
||||
|
||||
该优化会合并同一事件循环内的中间 UI 刷新,因此不保证逐条推送都触发一次 UI 回调;最终展示的数据保持为最新快照。网络接收、表格解析和实时订阅的吞吐不受本次修改限制。
|
||||
|
||||
## 验证结果
|
||||
|
||||
1. 使用现有依赖直接执行 Hvigor `assembleHap`,构建成功;子线程导入检查通过。
|
||||
2. 在 Pura 90 模拟器安装并启动应用,进入“行情 → 国内”,在“全部主力 / 能源”之间连续切换 20 轮,列表持续正常渲染。
|
||||
3. 离开行情页后查询应用崩溃日志,未发现 JS Crash 或 CppCrash。
|
||||
4. 同次验证临时记录了请求切换清理计数:每轮旧订阅由 2 降至 1、请求缓冲由 4 降至 3,且待通知数为 0;验证日志已删除,未纳入正式代码。
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# 交易 SDK 市价缓存去重与正确删除
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony_futures_trade_sdk` | `feature/FUHM-1979-match-push-optimization` | `fbfb85d704510fcbe94d1c58f732e0add4a76292` | 修复市价缓存重复写入和删除失效 |
|
||||
|
||||
## 问题
|
||||
|
||||
交易 SDK 的 `DefaultMarketPriceCacheCenter` 使用数组保存 `ContractMarketPrice`。原有 `removeCache()` 误用 `slice(i, 1)`,该调用只返回数组片段,不会修改原数组,因此已消费的缓存节点仍被单例缓存中心长期持有。
|
||||
|
||||
原有 `cachePrice()` 对相同 `ContractBean + MarketPriceType` 的价格也会无条件新增节点。交易页反复取价或刷新时,同一缓存键会持续产生重复对象;即使业务调用删除,旧节点仍无法从数组移除,导致缓存数组和 Local Heap 随调用次数增长。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. `removeCache()` 使用 `splice(i, 1)` 原地删除命中的缓存节点。
|
||||
2. `cachePrice()` 写入前查找相同合约和价格类型;命中时只更新最新价格并返回,不再追加重复节点。
|
||||
3. 验证阶段由主工程临时接入本地构建的 `FuTrade.har`,并在当前已安装依赖源码中同步修复,以支持不执行 `ohpm install` 的直接 Hvigor 构建;压力测试、测试入口和本地联调改动均不进入正式提交。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 生产代码修改位于兄弟仓库 `../harmony_futures_trade_sdk/FuTrade` 的市价缓存中心。
|
||||
- 主工程不修改交易协议、报价来源或下单流程;正式依赖仍保持 `1.0.13`,待交易 SDK 发布包含该修复的新版本后再升级版本号和锁定结果。
|
||||
- 缓存键仍为既有的 `ContractBean + MarketPriceType`;不同合约或价格类型之间不会互相覆盖。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
相同缓存键连续更新时,缓存节点数量从随调用次数线性增长收敛为最多一个;缓存被消费后会立即从数组移除。以本次 100 万次重复更新为例,节点数由潜在的 100 万个降至 1 个,删除后降至 0,重复节点消除率接近 100%。
|
||||
|
||||
根据 `ContractMarketPrice` 的对象字段、数组引用和价格字符串开销保守估算,100 万次重复更新可避免约 60~120 MB 的常驻堆增长;20 万次缓存/删除循环可避免约 12~24 MB 的残留。实际 PSS 和 JS Heap 收益受 Ark Runtime 对象布局、字符串表示及 GC 时机影响,仍应以修复前后的同场景 Profiler Trace 为准。
|
||||
|
||||
该修改只治理交易 SDK 市价缓存数组的重复节点和错误删除,不覆盖全局行情请求缓冲、实时订阅、整表解析与 UI 全量刷新等其他 OOM 链路。
|
||||
|
||||
## 验证结果
|
||||
|
||||
1. `FuTrade@default` 构建成功并生成新的 `FuTrade.har`;主工程 `entry@default` 和 `entry@ohosTest` 均通过直接 Hvigor 构建。
|
||||
2. 在 Pura 90 模拟器执行 100 万次相同缓存键更新,耗时 148 ms;最终读取到最新价格,删除后缓存为空。
|
||||
3. 在同一测试进程执行 20 万次缓存/删除循环,耗时 90 ms;循环结束后缓存为空。
|
||||
4. Instrumented Test 共执行 4 个用例,结果为 `Pass 4 / Failure 0 / Error 0`,总测试耗时 239 ms。
|
||||
5. 测试完成后未发现 `com.hexin.plat.hmn.futures` 的 OOM、JS Crash 或 CppCrash 记录。
|
||||
6. 提交前已清理主工程的本地 HAR 路径、`oh_modules` 同步修改、临时 Instrumented Test 接入和 SDK 测试代码,避免提交不可移植的联调依赖或压力测试代码。
|
||||
@@ -0,0 +1,37 @@
|
||||
# WebView 行情总览销毁时释放请求 Client
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `da5599387d82f06256e9224eb8c8001410de1a6b` | 补齐总览页面销毁解绑并断开 bridge 引用 |
|
||||
|
||||
## 问题
|
||||
|
||||
`OverViewEvent` 只处理 WebView 前后台切换,没有注册页面销毁回调。三个行情 client 已登记到全局 `RequestQueueManagement` 并写入请求缓冲后,如果 WebView 直接销毁,全局队列仍可能持有 client,client 又反向持有 `WebViewJavaScriptBridge`,形成跨页面 retained reference。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. 注册 `addAboutToDisappearCallBack()`,页面销毁时统一调用 `stopRequest()`。
|
||||
2. `stopRequest()` 在取消三个请求后清空每个 client 的 bridge 引用和 `OverViewEvent` 成员引用,使重复调用保持幂等。
|
||||
3. 销毁回调最后清空 `OverViewEvent.webViewJSBridge`,切断事件对象到 WebView bridge 的引用。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅修改行情总览 JS 事件的销毁生命周期。
|
||||
- 不修改三个行情协议的 frameId、pageId、请求文本、解析或回调数据结构。
|
||||
- 前后台恢复行为保持不变;已收到 JS 事件时仍会在回到前台后重新创建请求。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
每次总览页销毁会确定释放 3 个请求 client、对应请求缓冲和队列映射,避免其随反复进出页面线性累积。实际节省内存取决于 bridge、请求参数和页面对象引用图,本次没有设备堆快照,因此不估算具体 MB 或百分比。
|
||||
|
||||
该修复只覆盖 `OverViewEvent` 的三条行情请求,不代表其他 WebView JS 事件或全局行情链路已全部完成生命周期审计。
|
||||
|
||||
## 验证结果
|
||||
|
||||
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。
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# 通信 SDK 跨 Frame 请求缓冲清理与 HAR 接入
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `ohos_mobile_lib_communication` | `work-freeze-lib-communication-1.1.1` | `4784e4698a3d335471f275fd7c8dd9c113323b78` | 清理旧 frame 请求缓冲及关联客户端 |
|
||||
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `da5599387d82f06256e9224eb8c8001410de1a6b` | 替换主工程本地通信 HAR |
|
||||
|
||||
## 问题
|
||||
|
||||
通信 SDK 在 `frameId` 变化时调用的 `clearRequestPageList()` 为空实现,导致旧 `_sReqList`、`_sTempReqBuff`、`_sTemqReqList` 和队列 client 引用继续保留。重复请求还可能通过 instance 映射形成伴随 client,仅删除主 instance 不能完整释放引用链。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. SDK 在切换 frame 时收集旧缓冲的全部 instanceId,释放主 instance 及其伴随 instance。
|
||||
2. 清空三个缓冲并重置当前 frame,再按原流程写入新 frame。
|
||||
3. 保留切换前已登记的新 client,不使用破坏范围更大的全局队列清空。
|
||||
4. 构建新的 `lib_communication.har` 并替换主工程 `libs/lib_communication20250903.har`;依赖版本仍为 `1.1.1-beta.1`,lockfile 不变。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- SDK 修改位于 `../ohos_mobile_lib_communication/lib_communication`。
|
||||
- 主工程只替换本地通信 HAR,不修改调用 API 或请求协议。
|
||||
- `HQ2RequestManager`、实时行情整表复制和 UI 全量解析不在本次范围。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
跨 frame 切换后,请求缓冲由历史 frame 累积收敛为仅保留当前 frame;旧请求文本和 client 引用不再随切页次数线性增长。压力测试 20,000 轮后缓冲仍为 1 项,说明该场景的历史缓冲残留消除率为 100%。
|
||||
|
||||
上述结论只针对测试中的缓冲项和 client 计数,不等同于整进程 PSS 降幅;图片、Web、Native 堆及行情整表分配仍可能影响 OOM。
|
||||
|
||||
## 验证结果
|
||||
|
||||
1. SDK 实际源码压力测试执行 20,000 轮,每轮创建两个相同请求 client 并切换 frame;最终请求缓冲为 1、当前活跃 client 为 2,旧主/伴随 client 均为 0。
|
||||
2. 直接 Hvigor 构建 SDK Release HAR 成功,耗时 9.145 秒;接入产物 SHA-256 为 `b6919758c15761d5f9f75dcc0bf608d6ab2b46bd5ada9c92ddf1237be8b39621`。
|
||||
3. 主工程直接 Hvigor 构建 `entry@default` Debug HAP 成功,耗时 34.940 秒;未执行 `ohpm install`、安装或启动。
|
||||
4. 临时 API 24 配置、压力测试代码和日志均已清理;因本机缺少 API 20 SDK,API 20 构建与设备 OOM/PSS 验证未执行。
|
||||
5. 2026-08-21 在 Pura 90 API 24 模拟器执行有效的首页、行情、交易、发现四 Tab 定向循环 20 轮,每次切换等待 1 秒;进程持续存活,Crash 日志中未发现 OOM、JS Crash、CppCrash、SIGSEGV 或 SIGABRT。
|
||||
6. 设备有效循环前 Total PSS / ArkTS heap PSS / Native heap PSS 分别为 562,024 / 110,843 / 211,017 kB;20 轮后空闲 15 秒分别为 679,879 / 199,328 / 244,622 kB。整进程内存未回到基线,且生产包没有请求缓冲计数能力,因此该设备结果不能单独证明跨 frame 缓冲已释放,也不能据此将剩余增长归因于本链路;缓冲收敛结论仍以 20,000 轮白盒计数测试为依据。
|
||||
7. 首次尝试因首页 VIP 广告遮挡底部 Tab,点击未实际切页,该组数据已作废且未计入上述 20 轮结果。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 广告悬浮球 PixelMap 与 Dialog ID 生命周期清理
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures-apm-oom` | `work-20260820-clz-FUHM-1979-apm-oom` | `da5599387d82f06256e9224eb8c8001410de1a6b` | 释放广告 PixelMap、失效异步创建并清空 ID |
|
||||
|
||||
## 问题
|
||||
|
||||
`AdsBallManager` 创建完整图和半图 PixelMap 后未保存并显式释放所有权。任一图片加载失败时,另一张已成功创建的 PixelMap 会直接遗漏;弹窗销毁也只 dismiss dialog,不释放 Native 图片资源。`floatBallIds` 使用只增不删的数组,主题切换和广告刷新会长期保留历史 ID。
|
||||
|
||||
异步图片加载期间如果发生新的创建或销毁,旧任务还可能在完成后覆盖新弹窗,使旧图片和 dialog 生命周期错配。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. manager 明确持有当前完整图和半图 PixelMap,并在 dialog dismiss 后延迟 300 ms 释放,避免关闭动画仍使用图片。
|
||||
2. 单图失败、弹窗 show 失败和异步任务过期时立即释放已创建但未展示的 PixelMap。
|
||||
3. 使用 generation 标记使旧异步创建失效,避免旧结果覆盖新弹窗。
|
||||
4. `floatBallIds` 改为 `Set<string>`,销毁后清空;兜底 dismiss 仍按历史登记 ID 匹配当前页面 dialog。
|
||||
5. 对同一 PixelMap 被同时作为完整图和半图的异常情况只调用一次 `release()`。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅修改首页广告悬浮球的图片与 dialog 生命周期管理。
|
||||
- 不修改广告数据、图片地址、展示样式、点击行为或 300 ms 创建防抖。
|
||||
- 延迟释放只持有已 dismiss 的当前两张图片 300 ms,避免立即释放与弹窗关闭动画竞争。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
成功展示的每个悬浮球最多持有当前 2 张 PixelMap,销毁后释放;单图失败不再残留另一张;ID 集合在销毁后归零。Native 图片内存收益取决于图片尺寸、像素格式和系统解码缓存,本次未取得真实图片尺寸与设备 PSS,因此不估算具体 MB。
|
||||
|
||||
该修复不治理其他广告弹窗、网络图片缓存、系统图形缓存或 GPU/DMA 内存,也不代表 Native PSS 会在 release 后立即同步下降。
|
||||
|
||||
## 验证结果
|
||||
|
||||
1. 对实际 `AdsBallManager.ets` 转译后执行 5,000 轮成功创建/销毁、5,000 轮半图失败和 1 轮异步过期压力测试。
|
||||
2. 共创建 15,001 个模拟 PixelMap,全部恰好释放一次;最终 retained dialog ID 为 0、retained dialog 为 0。
|
||||
3. 新实现随主工程通过直接 Hvigor `entry@default` Debug HAP 构建,完整 ArkTS 编译、打包和签名成功;未执行安装。
|
||||
4. 压力测试中将 300 ms 延迟计时器同步触发以校验所有权计数;真实设备关闭动画时序、Native PSS 回落和 OOM 日志未验证。
|
||||
5. 临时压力测试代码与日志已删除,未在生产代码中保留调试计数。
|
||||
6. 2026-08-21 在 Pura 90 API 24 模拟器确认首页 VIP 广告真实展示并成功关闭,关闭后底部 Tab 恢复可操作;同次测试未发现 OOM、JS Crash、CppCrash、SIGSEGV 或 SIGABRT。
|
||||
7. 本次设备操作只完成 1 次真实广告展示/关闭,未取得 PixelMap Native 归属计数,也未完成 50~100 轮重复广告创建,因此不宣称真实设备 Native PSS 稳态已经验证;重复释放正确性仍以 15,001 个模拟 PixelMap 的白盒压力测试为依据。
|
||||
@@ -0,0 +1,38 @@
|
||||
# 通信库源码联调依赖说明
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `ohos_mobile_lib_communication` | `work-freeze-lib-communication-1.1.1` | `031d4cf221b4da6d9aaf2db443ba293559a0d4a5` | 通信 Worker 解码与队列优化(本说明所支撑的修复) |
|
||||
|
||||
> 本笔记为通信库源码联调/依赖说明,非生产修复本身;其支撑的修复见 `03`、`04`。
|
||||
|
||||
## 目的
|
||||
|
||||
解压与曲线修复基于 `lib_communication` `1.1.1-beta.1`,基线提交为 `7d3f126`,与应用原有 `lib_communication20250903.har` 的源码版本一致。性能验证期间曾将相邻仓库中的通信模块复制到当前工程 `lib_communication/`,以便临时加日志、打点和抓取 trace。
|
||||
|
||||
## 修改文件
|
||||
|
||||
- `oh-package.json5`
|
||||
- `build-profile.json5`
|
||||
- `base_ugc/oh-package.json5`
|
||||
- `biz_forecast/oh-package.json5`
|
||||
- `service_aigroup/oh-package.json5`
|
||||
- `lib_communication/`
|
||||
|
||||
## 接入配置
|
||||
|
||||
验证期间,根依赖和 `overrides` 使用 `file:./lib_communication`,模块内直接依赖使用 `file:../lib_communication`,根 `build-profile.json5` 将其注册为 HAR 模块。构建 `entry` 时直接编译这份通信源码。
|
||||
|
||||
新增 Worker 的运行时导入闭包在验证期间按最小范围登记到子线程门禁:
|
||||
|
||||
- `ConcurrentDecodeWorker.ts -> @ohos.worker`
|
||||
- `ConcurrentDecodeWorker.ts -> HXZip.ts`
|
||||
|
||||
## 影响与风险
|
||||
|
||||
本地源码保留原公开接口,包含 Worker 解压和曲线解析实现。该目录仅用于性能验证,不应作为正式交付方式。验证结束后已删除根 `build-profile.json5` 中的模块注册,并将根工程及三个业务模块的依赖恢复为 `libs/lib_communication20250903.har`。
|
||||
|
||||
## 验证
|
||||
|
||||
同一源码构建的通信库 debug HAR 及 `entry@default` debug HAP 均已完成构建、安装和运行验证。三轮 A/B 测试确认主线程 CPU 时间中位数下降 20.2%,详细数据见 `06-communication-worker-ab-performance.md`;正式交付仍需发布并接入包含修复的新版 HAR。
|
||||
@@ -0,0 +1,45 @@
|
||||
# 网络响应 XML 异步解析
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `e16813f86b8dbabcf6e6e4a18df72c80c69cc9db` | 认证 XML 解析迁移至 TaskPool(`ConcurrentXmlParser`) |
|
||||
|
||||
## 背景与目标
|
||||
|
||||
Top 20 冻屏日志中,主线程采样命中 Upass 响应的 GBK 解码和 `XmlPullParser.parse()`。进一步扫描发现验证码响应、账户手机号查询也在通信回调中同步解码并解析 XML。本次统一将三条活动链路迁移到 TaskPool,避免响应体异常放大时阻塞主线程。
|
||||
|
||||
## 修改范围
|
||||
|
||||
- `biz_common/src/main/ets/utils/xml/ConcurrentXmlParser.ets`
|
||||
- `biz_hxservice/src/main/ets/cookie/UpassSessionNetClient.ets`
|
||||
- `biz_auth/src/main/ets/auth/verifycode/CheckCodeLoginClient.ets`
|
||||
- `biz_auth/src/main/ets/auth/UserQueryClient.ets`
|
||||
- `claude_tool/subthread-import-guard/subthread-import-policy.json`
|
||||
|
||||
`biz_auth/src/main/ets/auth/UpassSessionNetClient.ets` 未发现导出或调用关系,属于当前不可达的遗留实现,本次不修改。
|
||||
|
||||
## 通用方案
|
||||
|
||||
新增 `parseXmlAttributes()` 并发方法,参数为原始字节、字符编码、目标属性列表和可选结束标签。TaskPool 内完成文本解码与 XML 扫描,统一关闭 DOCTYPE;没有指定结束标签时,获取全部目标属性后立即停止。主线程只处理返回值,不向子线程传递业务对象。
|
||||
|
||||
三处调用配置:
|
||||
|
||||
- Upass:`gbk`,提取 `sessionid`。
|
||||
- 验证码:`gbk`,提取 `code`、`msg`,遇到 `wlh_thsreg_modify` 结束标签停止。
|
||||
- 用户查询:`utf-8`,提取 `ckmobile`。
|
||||
|
||||
并发入口只包含 `@ohos.util` 和 `@ohos.xml` 两条运行时依赖边,已精确登记到子线程导入策略。
|
||||
|
||||
## 功能影响
|
||||
|
||||
- Upass SessionId 获取、持久化及 Cookie 建立。
|
||||
- 手机号登录或注册时的验证码结果回调。
|
||||
- 登录完成或推送更新时的账户手机号回填与偏好存储。
|
||||
|
||||
请求协议、公开回调接口和存储键不变。解析失败会进入原有失败处理;用户查询异步期间若发生用户切换,会丢弃旧响应。验证码日志不再输出完整 XML,只记录状态码和消息。
|
||||
|
||||
## 验证
|
||||
|
||||
`node claude_tool/subthread-import-guard/check-subthread-imports.mjs` 已通过:通用入口包含 1 个本地闭包文件和 2 条运行时依赖边。按当前要求未执行完整编译。运行期仍需覆盖 Upass 登录态、验证码成功/失败、手机号回填、用户切换和损坏 XML 场景,并确认主线程采样栈不再出现 XML 解析热点。
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# 通信解压迁移至 Worker
|
||||
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `ohos_mobile_lib_communication` | `work-freeze-lib-communication-1.1.1` | `031d4cf221b4da6d9aaf2db443ba293559a0d4a5` | `HXZip` 解压与曲线解析迁移到通信 HAR 内持久 Worker,并重构队列分发 |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `ce0719178530b5ca64272c6c1e90b5bcdb8297fc` | 重建并接入 `libs/lib_communication20250903-worker-opt.har` |
|
||||
|
||||
## 目的
|
||||
|
||||
冻屏采样栈显示主线程在 `HXZip.unzip()` 及解压后的行列转置循环中持续执行。压缩行情包越大,阻塞时间越长。
|
||||
|
||||
## 线上真实数据补充
|
||||
|
||||
2026-08-21 补充的 TOP10 耗时函数线上统计中,通信库仍有两条采样命中:
|
||||
|
||||
| TOP | 函数 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 1 | `writeReceiveTime entry\|@kernel/lib_communication\|1.1.1-beta.1\|src/main/ets/components/communication/protocol/MiniDataHead.ts` | 1 | 2 | 600ms |
|
||||
| 2 | `anonymous entry\|@kernel/lib_communication\|1.1.1-beta.1\|src/main/ets/request/QueueManagement.ts` | 1 | 2 | 600ms |
|
||||
|
||||
这两条不是 `HXZip.unzip()` 本身,但说明同一通信回调链路在线上仍能出现在主线程耗时采样中。`writeReceiveTime()` 只是写包头的短函数,单点优化价值低;`QueueManagement` 当前仍通过 `Map` 全量遍历完成注册、映射和分发查找,后续可将 `instanceId -> clients` / companion 映射拆成索引,减少行情响应分发阶段的线性扫描。
|
||||
|
||||
## TOP20 线上数据补充
|
||||
|
||||
同日 TOP20 统计中,通信库出现同一条分层回调链:
|
||||
|
||||
| TOP | 函数 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 7 | `anonymous entry\|@kernel/lib_communication\|1.1.1-beta.1\|.../handler/Response...` | 59 | 590 | 177000ms |
|
||||
| 8 | `fireConnectionRead entry\|@kernel/lib_communication\|1.1.1-beta.1\|.../connection/Abstract...` | 58 | 580 | 174000ms |
|
||||
| 9 | `connectionRead entry\|@kernel/lib_communication\|1.1.1-beta.1\|.../communication/handler/...` | 58 | 580 | 174000ms |
|
||||
| 10 | `invokeChannelRead entry\|@kernel/lib_communication\|1.1.1-beta.1\|.../connection/Abstract...` | 58 | 580 | 174000ms |
|
||||
|
||||
上述 4 项是同一次通信管线逐层转发的采样帧,不能将 699000ms 视为 4 个独立耗时问题。统计来自旧版 `1.1.1-beta.1`;当前工程已接入 Worker 解码和队列索引优化 HAR,需用新包重新采样确认是否仍上榜。
|
||||
|
||||
## 修改文件
|
||||
|
||||
- `lib_communication/.../protocol/ConcurrentDecodeClient.ts`
|
||||
- `lib_communication/.../protocol/ConcurrentDecodeWorker.ts`
|
||||
- `lib_communication/.../protocol/MobileDataProcessor.ts`
|
||||
- `lib_communication/.../protocol/ReceiveDataProcess.ts`
|
||||
- `lib_communication/.../handler/ResponseStructDecodeHandler.ts`
|
||||
- `lib_communication/build-profile.json5`
|
||||
|
||||
以上修改基于相邻的 `ohos_mobile_lib_communication` 源码仓库,验证时临时复制到当前工程,验证结束后已移除。
|
||||
|
||||
## 实现
|
||||
|
||||
通信库主体为 `.ts`,不能直接导入只允许在 `.ets` 中声明的 `@Concurrent` 任务。因此改为复用一个 HAR 内持久 Worker,通过请求 ID 匹配结果,在 Worker 中完成 `HXZip` 解压及行列转置。解码调用链改为异步;每个 `ResponseStructDecodeHandler` 使用 Promise 队列串行处理响应,避免并发完成导致消息乱序。
|
||||
|
||||
## 影响与风险
|
||||
|
||||
主线程只负责组装 `HXDataInputStream` 和继续分发。Worker 参数与结果均为可序列化的基础类型或 `Uint8Array`。单次消息仍受 16 MB 序列化限制,Worker 异常会拒绝等待中的请求,响应队列捕获错误后继续处理后续消息。
|
||||
|
||||
## 验证
|
||||
|
||||
基于 `1.1.1-beta.1` 的通信库 HAR 和默认 debug HAP 已构建、安装并完成行情周期切换验证。三轮 A/B 测试中,主线程 CPU 时间中位数下降 20.2%,8 ms 以上连续运行片段减少 53.3%;详细结果与限制见 `06-communication-worker-ab-performance.md`。
|
||||
@@ -0,0 +1,33 @@
|
||||
# 曲线解析迁移至 Worker
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `ohos_mobile_lib_communication` | `work-freeze-lib-communication-1.1.1` | `031d4cf221b4da6d9aaf2db443ba293559a0d4a5` | `HXZip` 解压与曲线解析迁移到通信 HAR 内持久 Worker |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `ce0719178530b5ca64272c6c1e90b5bcdb8297fc` | 重建并接入 `libs/lib_communication20250903-worker-opt.har` |
|
||||
|
||||
## 目的
|
||||
|
||||
冻屏日志显示曲线解析在主线程执行 `points × fields` 双重循环,并反复完成整数读取与 HXLong 浮点转换,是曲线点数较大时的 CPU 热点。
|
||||
|
||||
## TOP20 线上数据补充
|
||||
|
||||
TOP20 的 TOP7–TOP10 命中的是通信响应读取、连接转发和通道回调的分层函数,没有直接命中曲线解析函数。由于这些函数属于同一条响应管线,不能据此证明曲线解析仍是独立瓶颈;当前 Worker 版本的收益仍需使用新包按曲线大包场景重新采样确认。
|
||||
|
||||
## 修改文件
|
||||
|
||||
- `lib_communication/.../protocol/ConcurrentDecodeClient.ts`
|
||||
- `lib_communication/.../protocol/ConcurrentDecodeWorker.ts`
|
||||
- `lib_communication/.../protocol/MobileDataProcessor.ts`
|
||||
|
||||
## 实现
|
||||
|
||||
扩展数据仍按原逻辑解析;剩余曲线字节、字段类型和点数提交给通信 HAR 内的持久 Worker。子线程保持原协议的小端读取、`MD_TYPE_MASK` 分支、HXLong 符号/指数/空值语义,返回按字段排列的数值列。宿主线程仅按字段 ID 重建 `dataTable` 和 `typeTable`。
|
||||
|
||||
## 影响与风险
|
||||
|
||||
公开的 `StuffCurveStruct` 数据结构不变,重复字段 ID 仍以后出现的列为准。若实际单包接近 Worker 的 16 MB 序列化上限,需要进一步改为分块或共享缓冲区。
|
||||
|
||||
## 验证
|
||||
|
||||
基于 `1.1.1-beta.1` 的通信库 HAR 和默认 debug HAP 已构建、安装并完成行情周期切换验证。当前结果证明通信解析阶段的主线程负载下降,但 `CurveView` 刷新耗时和超时帧没有稳定改善;详细结果与限制见 `06-communication-worker-ab-performance.md`。
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# 交易 SDK Native 锁竞争应用侧优化
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `d04f7720c13b130bd9bdab69109d6a93c6edcfca` | 应用侧减少交易 SDK Native 锁竞争(`FuturesApiInterceptor`/`TradeApiManager`) |
|
||||
| `futures_trade_sdk`(原生 lib-weituosdk) | `work-clz-FUHM-1979-native-lock-opt` | `1ba241ef8d9aae16dde1b0c4e406d6cd41cca216` | 链接复用计数由 `recursive_mutex` 改为 `std::atomic`,降低锁竞争 |
|
||||
|
||||
Native 锁优化 SO(`futures-trade-sdk-so-1.2.0-lock-opt.har`)由上述 SDK 提交构建(lib-weituosdk 的 `futures_spi_api_manager.cpp`/`futures_quants_spi.cpp`/`.h`)。应用侧止血见上表首行;本工程 `oh-package.json5` 指向该 SO 的接入改动尚未提交。
|
||||
|
||||
|
||||
|
||||
## 问题
|
||||
|
||||
冻屏日志显示,主线程调用交易 SDK 时阻塞在 `getReuseSpiApiByMode`、`hasLoggingInRequest` 等 Native 锁上。高频入口包括交易请求发送前检查和自动重登检查。
|
||||
|
||||
## 线上真实数据补充
|
||||
|
||||
2026-08-21 补充的 TOP10 耗时函数线上统计中,交易 SDK 发送链路仍被采样命中:
|
||||
|
||||
| TOP | 函数 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 7 | `sendMsg entry\|@b2c-f/futures_ohos_trade_sdk\|1.0.13\|src/main/ets/trade/api/rpc/RspSendMsgCenterImpl.ts` | 1 | 10 | 3000ms |
|
||||
| 4 | `isNumber entry\|@b2c-f/futures_ohos_trade_sdk\|1.0.13\|src/main/ets/trade/util/StringUtil.ts` | 1 | 2 | 600ms |
|
||||
|
||||
`sendMsg()` 是交易请求进入 SDK/Native 的关键同步入口,和本 notes 记录的 Native 锁竞争止血方向一致。`isNumber()` 为短函数,单独看不像根因,更可能是交易列表或请求参数处理链路中的采样帧;应结合完整调用栈判断是否被 `sendMsg()` 或列表刷新长任务包裹。
|
||||
|
||||
同日 TOP20 统计进一步命中交易 SDK 的两组调用链:
|
||||
|
||||
| TOP | 函数/调用链 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 1–4 | `getReuseIpForLogin → getRequestIpForLogin → libweituo_sdk_ohos.so` | 75 | 750 | 900000ms(分层重复采样) |
|
||||
| 5–6 | `FuturesApiInterceptor.interceptSendMsg*` | 67/64 | 670/640 | 201000/192000ms |
|
||||
| 11–18 | `enqueue → awaitEnqueue → innerEnqueue → startSendRequest → proceedSendMsg` | 47–48 | 470–480 | 141000–144000ms/项(分层重复采样) |
|
||||
| 19–20 | `TradeApiManager.tryToReLoginAccount` 及其匿名闭包 | 44 | 440 | 132000ms/项 |
|
||||
|
||||
TOP1–4 和 TOP19–20 共同指向自动重登录入口的同步复用 IP 查询;TOP11–18 是同一请求链的多层采样,不能直接累加为独立损耗。该 TOP20 文件记录的是旧版 `1.0.11/1.1.1`,当前工程已接入 `1.0.13/1.2.0` 锁优化 SO;应用侧仍保留首次同步 `getReuseIpForLogin()` 风险,需新包线上复采确认。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. 删除 `FuturesApiInterceptor` 中无实际拦截效果的请求前检查。原逻辑会在每次交易请求前同步查询登录中、在线状态,但两个判断分支中的拦截代码均已注释。删除后不改变当前请求行为,同时避免业务请求重复进入 Native 锁域。
|
||||
2. `TradeApiManager` 按交易账号复用正在执行的重登 Promise。同一账号由认证、交易页或推送重复触发重登时,只执行一次 SDK 登录,其余调用复用结果并保留各自成功、失败回调。
|
||||
3. 在线检查和复用连接查询前先判断该账号是否正在重登,避免登录过程中再次同步查询 Native 状态。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 交易接口发送、自动重登、推送触发的在线检查和交易页切换重登。
|
||||
- 不修改交易 SDK、Native SO、登录参数、失败重试次数及成功/失败业务处理。
|
||||
- 不将 SDK 对象迁移到 Worker 或 TaskPool,不改变其线程归属。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
优化可减少主线程进入交易 SDK 互斥锁的频率,并阻止同账号重复登录放大锁竞争。`tryToReLoginAccount` 首次判断仍需调用一次 `getReuseIpForLogin`,因此这属于应用侧止血;彻底消除锁等待仍需要 SDK 缩小 Native 锁范围并发布新的 SO。
|
||||
|
||||
## 验证建议
|
||||
|
||||
在持续发送交易请求时反复执行断网重连、前后台切换、推送唤起和交易账号切换,确认同账号并发登录数不超过 1,并检查冻屏主线程栈中是否仍出现 `mutex::lock → getReuseSpiApiByMode/hasLoggingInRequest`。
|
||||
@@ -0,0 +1,39 @@
|
||||
# 通信 Worker A/B 性能验证
|
||||
## 验证对象(对应修复)
|
||||
|
||||
该验证针对 `HXZip` 解压与曲线解析迁移至 Worker 的收益,属于笔记 `03`、`04` 的量化回放,完整修复提交见 `03`/`04`(`ohos_mobile_lib_communication` `031d4cf221b4da6d9aaf2db443ba293559a0d4a5`、`harmony-ths-futures` `ce0719178530b5ca64272c6c1e90b5bcdb8297fc`)。
|
||||
|
||||
## 验证目标
|
||||
|
||||
对比 `lib_communication` `1.1.1-beta.1` 在同步解压/曲线解析与 Worker 异步处理下的主线程负载。测试只评价通信解压和曲线解析改造,不包含 XML、交易 SDK 或图表绘制优化。
|
||||
|
||||
## 测试方法
|
||||
|
||||
- 时间:2026-08-18
|
||||
- 设备:Pura 90 手机模拟器,`127.0.0.1:5555`
|
||||
- 页面:国内行情“乙二醇2609”详情页
|
||||
- 操作:依次点击 `1分 -> 5分 -> 15分 -> 日K -> 分时`,连续执行 3 组,共 15 次切换
|
||||
- 轮次:旧同步版和 Worker 新版各 3 轮;每轮重新启动应用进程并采集 20 秒 hitrace
|
||||
- 分析:使用 DevEco `trace_streamer` 转换为 SQLite,统计目标进程 `lat.hmn.futures` 的主线程调度片段
|
||||
|
||||
## 测试结果
|
||||
|
||||
下表使用三轮中位数,括号内为三轮范围。
|
||||
|
||||
| 指标 | 旧同步版 | Worker 新版 | 变化 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| 主线程 CPU 时间 | 2959 ms(2923~3309) | 2361 ms(2330~2512) | -20.2% |
|
||||
| 进程总 CPU 时间 | 3765 ms(3731~4201) | 3075 ms(3069~3250) | -18.3% |
|
||||
| 主线程 `>= 8 ms` 连续运行片段 | 30 次(23~36) | 14 次(14~17) | -53.3% |
|
||||
| 主线程调度片段 P99 | 5.747 ms(5.304~5.865) | 4.644 ms(4.452~4.692) | -19.2% |
|
||||
| 主线程最大连续运行片段 | 23.698 ms(13.252~31.006) | 19.360 ms(12.924~24.345) | -18.3% |
|
||||
|
||||
## 结论与限制
|
||||
|
||||
Worker 改造使主线程计算量下降约 20%,8 ms 以上连续运行片段减少一半以上,主要收益是降低大包解压和曲线解析占用主线程导致的冻屏风险。
|
||||
|
||||
`CurveView` 刷新总耗时中位数由 221.5 ms 变为 236.0 ms,没有稳定改善。超过 16.67 ms 的帧中位数由 9 次变为 13 次,帧尾部数据也未呈现一致收益。因此本次结果不能解释为整体帧率提升,优化范围仅限通信解析阶段。
|
||||
|
||||
自动框架识别未生成应用绘帧记录,因此未套用 ArkUI 丢帧分类结果。后续建议在真机上使用固定响应包、增加解压和曲线解析起止 trace 点,并扩大轮次,以进一步隔离网络波动、模拟器调度和图表渲染噪声。
|
||||
|
||||
原始 trace 和 SQLite 数据未纳入仓库,测试时临时保存在 `/tmp/sdk-worker-ab.psA7Sp/`。
|
||||
@@ -0,0 +1,63 @@
|
||||
# 交易 SDK 成交推送去重与排序优化
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `ce0719178530b5ca64272c6c1e90b5bcdb8297fc` | 接入本地 `libs/FuTrade-1.0.13-match-push-opt.har`(含成交推送去重与排序) |
|
||||
| `harmony_futures_trade_sdk` | `feature/FUHM-1979-match-push-optimization` | `9e56923cfaadb14fff0fa928cabe4e5aef615490` | 优化成交推送处理(去重与稳定排序) |
|
||||
| `harmony_futures_trade_sdk` | `feature/FUHM-1979-match-push-optimization` | `31256eff45b51a96551a2ba8f2b31bb02e4258af` | 优化推送缓存查重 |
|
||||
|
||||
SDK 侧成交推送去重/排序来源于 `harmony_futures_trade_sdk` 仓库上述源码提交,本仓库以本地 HAR 携带,接入提交见上表首行。
|
||||
|
||||
## 问题
|
||||
|
||||
成交推送进入 `MatchOrderSummary.updateMatchOrders()` 时,旧逻辑需要遍历全部成交记录判断重复成交,并在每次新增后对完整数组执行一次排序。成交推送频繁或历史成交较多时,会重复消耗 CPU,并增加成交列表更新的耗时。
|
||||
|
||||
## 线上真实数据补充
|
||||
|
||||
2026-08-21 补充的 TOP10 耗时函数线上统计没有直接命中 `MatchOrderSummary.updateMatchOrders()`,但命中了同属交易 SDK 的 `RspSendMsgCenterImpl.sendMsg()`:1 次故障、10 次调用、总耗时 3000ms。该数据不能证明成交推送优化已覆盖此线上样本,只能说明交易 SDK 1.0.13 仍存在主线程高频 SDK 调用采样;成交推送优化需要继续依赖新 HAR 接入后的成交推送专项回归和新日志闭环。
|
||||
|
||||
TOP20 统计同样没有直接命中 `MatchOrderSummary.updateMatchOrders()`。TOP11–TOP18 命中的是交易请求发送链的 `enqueue/awaitEnqueue/proceedSendMsg` 分层函数,属于请求排队和发送链路,不足以证明成交推送去重与排序优化已经生效或失效;仍需通过成交推送专项采样验证。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. 在 `MatchOrderSummary` 中增加 `Set<string>`,缓存已有成交的 `matchIdentify().identify`。重复推送直接通过 `Set.has()` 过滤,避免遍历成交数组。
|
||||
2. 首次新增推送仍执行完整排序,确保查询结果初始顺序不确定时可以恢复到成交排序规则。
|
||||
3. 首次排序完成后,后续推送使用二分查找确定插入位置,再插入 `matchOrderRsps`,避免每次对全部成交记录重新排序。
|
||||
4. 二分插入在比较结果相同时继续向后查找,使相同成交时间和开平方向的记录保持稳定顺序。
|
||||
5. 增加单元测试,覆盖重复成交、稳定排序插入和不同账号推送过滤。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 交易 SDK 的成交汇总、成交推送和成交列表排序。
|
||||
- 当前项目通过本地 `libs/FuTrade-1.0.13-match-push-opt.har` 使用该版本,依赖配置位于根目录 `oh-package.json5`。
|
||||
- 成交量、平仓盈亏、盯市平仓盈亏的汇总规则不变。
|
||||
- 账号过滤规则和成交唯一标识规则不变;`Set` 仅用于查重,不参与成交列表排序。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
重复推送判断由数组遍历变为近似 O(1),后续新增成交由全量排序变为二分查找加数组插入。优化主要针对高频成交推送和较大历史成交列表,业务展示顺序不应发生变化。
|
||||
|
||||
`matchOrderRsps` 仍是公开可变数组。如果外部代码直接修改、排序或替换数组内容,可能导致成交数组、去重 `Set` 和有序状态不一致;业务代码应通过 SDK 的成交汇总接口读取,避免直接修改该数组。初始查询数据中的重复项也不会由本次改动主动清理。
|
||||
|
||||
## 验证建议
|
||||
|
||||
### 单元测试
|
||||
|
||||
在交易 SDK 的 `FuTrade` 模块运行 `MatchOrderSummary` 测试,确认以下场景:
|
||||
|
||||
- 相同成交推送只保留一条,汇总值不重复增加;
|
||||
- 首次推送后成交记录按既有时间和开平方向规则排序;
|
||||
- 后续推送插入到正确位置,相同排序值保持稳定顺序;
|
||||
- 不同账号的成交推送被过滤;
|
||||
- 空初始列表、白盘/夜盘边界和不同开平方向均可正常插入。
|
||||
|
||||
### 当前项目验证
|
||||
|
||||
确认 `oh-package.json5` 指向本地 HAR 后,执行:
|
||||
|
||||
```bash
|
||||
./claude_tool/claude_compile.sh
|
||||
```
|
||||
|
||||
并在成交页面回归查询成交、接收新成交推送、重复推送和账号切换,检查成交条数、成交量、盈亏汇总及列表顺序均保持正确。
|
||||
@@ -0,0 +1,55 @@
|
||||
# 交易页前台在线状态查询改为读缓存
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `8c3170778f50bf767c9146ea177e82080d629da2` | 交易页前台在线状态改为读缓存 `currentAccount.isOnline` |
|
||||
|
||||
## 问题
|
||||
|
||||
冻屏日志聚类 20020090(3 次):应用切回前台触发 `TradePage.isAbilityForeGroundChanged()` 时,主线程同步调用 `ApiCommon.isOnlineAccount()`,经 JSI 桥穿透到 `libweituo_sdk_ohos.so` 的 `SpiApiManager::getLoginSuccSpiApi` 并等待 `recursive_mutex`。当 SDK 网络线程(asio 断线/连接回调链路)持有该锁时,主线程卡在 `__timedwait_cp` 等锁 3.1/6.2 秒,3S/6S 抓栈逐帧一致,触发冻屏上报。
|
||||
|
||||
交易 SDK 1.0.13 的 `TradeAccount` 已维护应用侧在线状态:登录成功(`setLoginSuccess`)、断网(`checkAccountOnline`)、重登成功/失败(`TradeApiManager`)及退出登录(`resetUnLogined`)时经 `updateOnlineStatus()` 更新 `isOnline` 字段并广播 `ACCOUNT_ONLINE_STATUS_CHANGED` 事件。交易页已监听该事件,前台切换所需的"是否在线"用缓存即可回答,无需同步穿透 Native 查询。
|
||||
|
||||
## 线上真实数据补充
|
||||
|
||||
2026-08-21 补充的 TOP10 耗时函数线上统计中,交易页前台链路仍被采样命中:
|
||||
|
||||
| TOP | 函数 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 10 | `onForeground entry\|biz_trade\|1.0.0\|src/main/ets/views/tab/TradeGuaDanListView.ts` | 1 | 10 | 3000ms |
|
||||
|
||||
该样本不是 `TradePage.isAbilityForeGroundChanged()` 的同一函数,但同属交易页前台恢复路径。当前 `TradeGuaDanListView.onForeground()` 会立即触发 `GuaDanViewModel.sendRequest()`,其中包含条件单标签查询和挂单查询;如果前后台切换、刷新和交易推送相互叠加,仍可能造成主线程连续调度和 SDK 请求入口放大。后续可对前台刷新增加去重、节流或复用正在进行的查询。
|
||||
|
||||
## TOP20 线上数据补充
|
||||
|
||||
TOP20 未直接命中 `TradeGuaDanListView.onForeground()`,但命中了交易页自动重登录入口:
|
||||
|
||||
| TOP | 函数 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 19 | `tryToReLoginAccount entry\|biz_trade\|1.0.0\|.../TradeApiManager.ts` | 44 | 440 | 132000ms |
|
||||
| 20 | `anonymous entry\|biz_trade\|1.0.0\|.../TradeApiManager.ts` | 44 | 440 | 132000ms |
|
||||
|
||||
该结果说明前台恢复、推送和交易页刷新仍可能汇聚到自动重登录链路,但两个函数属于同一入口的分层采样,不能重复计为两项独立耗时。`TradePage` 的在线状态查询已改为读缓存;`TradeApiManager` 的同步复用 IP 查询仍需结合新版 SDK 线上采样继续确认。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. `TradePage.isAbilityForeGroundChanged()` 中的 `ApiCommon.isOnlineAccount(currentAccount)` 替换为读缓存 `currentAccount.isOnline`。
|
||||
2. 删除 `TradePage` 中不再使用的 `ApiCommon` import(全文件仅此一处使用)。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 交易页前台切换时在线状态判断的数据来源,由 Native 同步查询改为应用侧缓存。
|
||||
- 条件单查询时机不变:在线时立即 `checkConditionTriggeredForCurrentAccount()`,离线时置 `needQueryConditionTriggeredAfterTradeLogin`,登录成功后由 `onTradePushLoginResult`、切换账户后由 `onTradeLogin` 补查。
|
||||
- 不修改交易 SDK、Native SO、`isOnline` 状态的维护链路及其事件广播。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
消除该主线程等锁入口,前台切换不再同步进入交易 SDK 锁域。`isOnline` 为应用侧维护状态,SDK 断线回调尚未到达的极短窗口内可能读到旧值,最多导致多查一次条件单(请求失败无害);彻底消除等锁风险仍需要 SDK 缩小 Native 锁范围并发布新的 SO。
|
||||
|
||||
## 验证建议
|
||||
|
||||
1. 执行 `./claude_tool/claude_compile.sh` 编译通过。
|
||||
2. 真机登录后反复前后台切换,确认条件单查询行为与改动前一致,hilog 中不再出现主线程 `isLoggedInAccount` 同步调用。
|
||||
3. 断网后回前台、恢复网络、重登成功,确认 `onTradePushLoginResult` 兜底补查条件单的路径正常。
|
||||
4. 检查冻屏主线程栈中是否仍出现 `mutex::lock → SpiApiManager::getLoginSuccSpiApi`。
|
||||
@@ -0,0 +1,35 @@
|
||||
# 画线绘制候选集去重 O(n²) → O(n)
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `hmdrawlinebasicsdk` | `work-clz-fuhm-1979-drawline-dedup` | `c3f8e6bc2ca44af9afa9bf0163d88d2508d55a0a` | 画线绘制候选集去重 O(n²)→O(n) |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `ce0719178530b5ca64272c6c1e90b5bcdb8297fc` | 接入 `libs/drawlinebasic-1.2.1-dedup-opt.har` 及主工程 overrides |
|
||||
|
||||
## 问题
|
||||
|
||||
冻屏聚类 20020217(5 次)、20020340(3 次):主线程栈停在 `DrawingApiImpl.appendUniqueLines → findIndex → isSameLine`。`drawLines()` 每帧渲染都会调用 `getLinesToDraw()` 重建绘制候选集,其中 `appendUniqueLines` 用 `source.forEach` 嵌套 `target.findIndex` 判重,复杂度 O(n·m)。画线数量较多或跨周期线合并时,主线程持续繁忙触发 3S/6S 冻屏上报。
|
||||
|
||||
判重语义(原 `isSameLine`):同对象引用、本地 ID 或远端 ID 任一非空且相等即判为同一条线。注意本地 ID 不同但远端 ID 相同的两条线也判同一条,因此不能用单一 key 去重。
|
||||
|
||||
## 优化方案
|
||||
|
||||
1. `appendUniqueLines` 改为三个 Set(对象引用 `refSet`、本地 ID `localIdSet`、远端 ID `lineIdSet`)一次遍历完成判重,复杂度降为 O(n);判重语义与原 `isSameLine` 逐条等价,目标列表插入顺序不变;原私有方法 `isSameLine` 随之删除。
|
||||
2. 补丁基于 `hmdrawlinebasicsdk` 仓库 `feature-zyh-20260708-FUHM-1578-cross-period` 分支(画线 SDK 1.2.1 的发布源,与 ohpm 发布包源码树逐字节一致;master 仍为 1.1.2,无此代码),仓库内 commit `c3f8e6b`,本地构建产物为 `libs/drawlinebasic-1.2.1-dedup-opt.har`。
|
||||
3. 主工程三处接入:根 `oh-package.json5` 依赖与 `overrides`、`biz_quote/oh-package.json5` 依赖均改为 `file:` 引用本地 HAR。`overrides` 同时将 `drawlinepane@1.1.0` 传递依赖的 `drawlinebasic@1.2.0` 强制指向同一本地 HAR,保证最终产物只有一份 drawlinebasic。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅画线绘制候选集的去重实现;画线数据模型、持久化、云端合并、绘制样式与命中逻辑不变。
|
||||
- 每帧绘制候选集重建仍会分配三个 Set,列表本身不缓存;进一步可考虑按 cache 版本缓存合并结果(本次未做)。
|
||||
|
||||
## 预期收益与边界
|
||||
|
||||
候选集去重由 O(n·m) 降为 O(n),画线数量越大收益越明显,直接消除该冻屏聚类的热点函数。判重语义与原实现逐条等价(含"本地 ID 不同但远端 ID 相同判同一条"的边界情形),重复描边、线条层级行为不变。画线 SDK 侧彻底根治仍建议上游将 `getLinesToDraw` 的合并结果按缓存版本缓存,避免每帧重建。
|
||||
|
||||
## 验证建议
|
||||
|
||||
1. 执行 `./claude_tool/claude_compile.sh` 编译通过。
|
||||
2. 真机在画线较多(数十条以上)的 K 线页面滑动、切换周期,确认绘制行为与改动前一致,冻屏主线程栈不再出现 `appendUniqueLines → findIndex`。
|
||||
3. 回归跨周期画线:新建、云端合并、编辑态切换、删除场景下无重复描边,线条层级与命中结果不变。
|
||||
4. 检查最终产物中 `@b2c-f/drawlinebasic` 仅存在一份(overrides 生效)。
|
||||
@@ -0,0 +1,39 @@
|
||||
# TOP10 线上耗时函数后续候选项
|
||||
## 修复说明
|
||||
|
||||
本笔记为线上 TOP10 耗时函数的分析、候选项与建议修复顺序,未包含生产修复本身;候选的实现见 **`11-top10-followup-fixes.md`**(对应本仓库 commit `0d10cf71a01c8c5d875ca4c950d6d2fd71d3fd42`)。
|
||||
|
||||
## 数据来源
|
||||
|
||||
2026-08-21 从 `~/Downloads/TOP10耗时函数列表.md` 补充线上真实 TOP10 耗时函数统计。该列表是函数采样聚合,不等价于完整冻屏根因;短函数命中通常表示它处在更长的调用链上,需要结合完整 faultlog/采样栈判断。
|
||||
|
||||
| TOP | 函数 | 故障出现次数 | 调用次数 | 总耗时 |
|
||||
| ---: | --- | ---: | ---: | ---: |
|
||||
| 1 | `writeReceiveTime entry\|@kernel/lib_communication\|1.1.1-beta.1\|src/main/ets/components/communication/protocol/MiniDataHead.ts` | 1 | 2 | 600ms |
|
||||
| 2 | `anonymous entry\|@kernel/lib_communication\|1.1.1-beta.1\|src/main/ets/request/QueueManagement.ts` | 1 | 2 | 600ms |
|
||||
| 3 | `anonymous entry\|@b2b/hq_table\|1.0.0-rc.9\|src/main/ets/model/data/TableDataSource.ts` | 1 | 10 | 3000ms |
|
||||
| 4 | `isNumber entry\|@b2c-f/futures_ohos_trade_sdk\|1.0.13\|src/main/ets/trade/util/StringUtil.ts` | 1 | 2 | 600ms |
|
||||
| 5 | `transFormServerColor entry\|@b2c/lib_baseui\|1.0.0\|src/main/ets/utils/ColorUtils.ts` | 1 | 2 | 600ms |
|
||||
| 6 | `isArbitrageEnabled entry\|biz_trade\|1.0.0\|src/main/ets/trade/manager/TradeCustomTabManager.ts` | 1 | 4 | 1200ms |
|
||||
| 7 | `sendMsg entry\|@b2c-f/futures_ohos_trade_sdk\|1.0.13\|src/main/ets/trade/api/rpc/RspSendMsgCenterImpl.ts` | 1 | 10 | 3000ms |
|
||||
| 8 | `showOrderNameCell entry\|biz_trade\|1.0.0\|src/main/ets/views/tab/ContractNameCellView.ts` | 1 | 4 | 1200ms |
|
||||
| 9 | `anonymous entry\|biz_futures_chart\|1.0.0\|src/main/ets/view/View.ts` | 1 | 6 | 1800ms |
|
||||
| 10 | `onForeground entry\|biz_trade\|1.0.0\|src/main/ets/views/tab/TradeGuaDanListView.ts` | 1 | 10 | 3000ms |
|
||||
|
||||
## 当前判断
|
||||
|
||||
- `hq_table` 表格数据合并是优先级最高的新增候选:`TableDataSource.notifyDataSetChanged()` 仍在主线程逐行 merge、创建默认行并触发可见行更新,TOP3 达到 10 次调用、3000ms。
|
||||
- `lib_communication` 队列管理仍有结构性优化空间:`QueueManagement` 通过遍历 `Map<ITCPClient, Map<InstanceId, InstanceId[]>>` 完成注册、删除、映射和分发查找,可改为维护 `instanceId` 索引减少线性扫描。
|
||||
- `biz_trade` 套利开关可做缓存:`isArbitrageEnabled()` 会进入 `TradeFunctionSwitch.isSupportArbitrageTrade()` 和 `GrayTestManager.isFeatureEnable()`,灰度命中时包含日志和 `JSON.stringify(grayConfig)`,不适合在列表构建中高频调用。
|
||||
- `ContractNameCellView.showOrderNameCell()`、`ColorUtils.transFormServerColor()`、`StringUtil.isNumber()` 和 `BaseView` 绘制/布局闭包本身都较短,当前不应只针对短函数微调,应先定位其上层列表刷新、图表刷新或交易请求链路。
|
||||
|
||||
## 建议修复顺序
|
||||
|
||||
1. 在 `hq_table` 源码侧优化 `TableDataSource.notifyDataSetChanged()`:减少全量行合并、只更新可见区、批量合并 `notifyChange()`,必要时将可 Sendable 的数据预处理继续前移到子线程。
|
||||
2. 在通信 SDK 源码侧重构 `QueueManagement` 索引:新增 `instanceId -> clients` 和 companion 映射,保留原公开接口,避免响应分发时扫描全部客户端。
|
||||
3. 在 `TradeCustomTabManager` 或 `TradeFunctionSwitch` 增加账号维度套利灰度缓存,并在灰度配置变更、账号切换、设置变更时失效。
|
||||
4. 对 `TradeGuaDanListView.onForeground()` 的挂单刷新做去重/节流,避免前台恢复、手动刷新和推送触发重复查询。
|
||||
|
||||
## 验证口径
|
||||
|
||||
每个候选项修复后,需要用新包重新采集线上或真机冻屏采样,确认对应函数不再进入 TOP 耗时列表。仅凭本次 TOP10 采样不能判定短函数为唯一根因。
|
||||
@@ -0,0 +1,90 @@
|
||||
# TOP10 线上耗时函数后续修复
|
||||
## 修复提交
|
||||
|
||||
| 仓库 | 分支 | Commit ID | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `harmony-ths-futures` | `work-20260818-clz-FUHM-1979-apm-appfreeze` | `0d10cf71a01c8c5d875ca4c950d6d2fd71d3fd42` | 挂单请求合并、套利开关缓存、通信队列索引、行情表格延迟合并 |
|
||||
| `ohos_mobile_lib_communication` | `work-freeze-lib-communication-1.1.1` | `031d4cf221b4da6d9aaf2db443ba293559a0d4a5` | `QueueManagement` 队列索引(instanceId→client / companion) |
|
||||
|
||||
## 问题来源
|
||||
|
||||
基于 2026-08-21 `TOP10耗时函数列表.md` 的线上采样,继续处理仍有明确优化空间的主线程热点:
|
||||
|
||||
- `@b2b/hq_table` `TableDataSource`:1 次故障、10 次调用、总耗时 3000ms。
|
||||
- `@kernel/lib_communication` `QueueManagement`:1 次故障、2 次调用、总耗时 600ms。
|
||||
- `biz_trade` `TradeCustomTabManager.isArbitrageEnabled()`:1 次故障、4 次调用、总耗时 1200ms。
|
||||
- `biz_trade` `TradeGuaDanListView.onForeground()`:1 次故障、10 次调用、总耗时 3000ms。
|
||||
|
||||
## TOP20 线上数据补充
|
||||
|
||||
同日 `/Users/clz/Downloads/TOP20耗时函数列表.md` 汇总数据如下:故障出现次数 `1134`,调用次数 `11340`,记录总耗时 `3402000ms`。所有记录的单个故障最大耗时均为 `3000ms`。这些数值是采样记录的分层汇总,不代表一次冻屏实际耗时,不能直接相加计算收益。
|
||||
|
||||
TOP20 主要归并为以下调用链:
|
||||
|
||||
| 调用链 | TOP | 故障/调用 | 总耗时 | 当前判断 |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| 重登录复用 IP 与 Native 锁 | 1–4、19–20 | 75/750;44/440 | 900000ms;132000ms/项 | 锁优化 SO 已接入,应用侧仍有同步查询边界,待新包复采 |
|
||||
| 交易请求拦截与发送 | 5–6、11–18 | 47–67/470–670 | 141000–201000ms/项 | 已移除拦截器同步状态检查,通用请求仍需观察并发放大 |
|
||||
| 通信响应回调转发 | 7–10 | 58–59/580–590 | 174000–177000ms/项 | 属同一管线分层采样,Worker/索引优化已接入,待新包复采 |
|
||||
|
||||
TOP20 显示的交易 SDK 与通信库版本分别为旧版 `1.0.11/1.1.1` 和 `1.1.1-beta.1`;当前工程依赖已更新为交易 SDK `1.0.13/1.2.0` 及 Worker 优化通信 HAR。因此本表用于记录线上问题来源和修复映射,不能作为当前版本仍全部复现的证据。
|
||||
|
||||
## 修复内容
|
||||
|
||||
1. `GuaDanViewModel.sendRequest()` 增加进行中请求合并:挂单查询未返回时,后续前台/刷新触发只记录一次尾随刷新,避免短时间重复进入 SDK 查询链路。
|
||||
2. `TradeCustomTabManager.isArbitrageGrayOpened()` 增加当前账号维度缓存;`TradeSettingEvents.SETTING_UPDATE` 触发时失效,避免列表构建高频进入 `GrayTestManager.isFeatureEnable()` 的日志和配置序列化路径。
|
||||
3. 通信 SDK `QueueManagement` 增加 `instanceId -> client` 和 companion 映射索引,`getNetworkClient()` 从全量扫描改为按索引取主请求和 companion 请求。
|
||||
4. `hq_table_20260616.har` 内 `TableDataSource` 对非可见区 `SendableRow` 做延迟合并:当前刷新只合并可见行,非可见行缓存到 `pendingSendableRows`,滚动取数时再落到 `RowData`。
|
||||
5. 重新构建通信 HAR,并替换当前工程 `libs/lib_communication20250903-worker-opt.har`;`hq_table` 无相邻源码仓库,本次直接以最小差异更新现有 HAR。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 挂单列表前台恢复、手动刷新和交易推送叠加时的请求频率。
|
||||
- 独立交易页期权/套利 Tab 显示判断。
|
||||
- 通信响应分发中的请求匹配。
|
||||
- 行情表格 Sendable 数据进入主线程后的行合并时机。
|
||||
|
||||
业务协议、接口签名、表格展示数据结构和交易请求返回处理不变。挂单请求合并保留尾随刷新,避免丢掉进行中期间发生的最后一次刷新诉求。
|
||||
|
||||
## 验证与压测
|
||||
|
||||
- `node claude_tool/subthread-import-guard/check-subthread-imports.mjs`:PASS。
|
||||
- 通信 SDK:`devecocli build --modules lib_communication@default`:BUILD SUCCESSFUL。
|
||||
- 主工程:`./claude_tool/claude_compile.sh`:BUILD_SUCCESS。
|
||||
- 模拟器:Pura 90,`127.0.0.1:5555`,HarmonyOS 6.1.1(24)。
|
||||
- `devecocli run --module entry --device 127.0.0.1:5555 --product default --build-mode debug --skip-build`:安装并启动成功。
|
||||
- 应用进程 `com.hexin.plat.hmn.futures` 存活,前台任务 `EntryAbility` 存在。
|
||||
- 前后台压力:Home/重新拉起 20 轮完成,无 crash、无 `APPFREEZE` 日志。
|
||||
- UI 基础场景:行情 Tab 切换、行情页上下滑动 8 轮、交易 Tab 切换、交易入口前后台 5 轮完成,无 crash、无 `APPFREEZE` 日志。
|
||||
- 备注:未登录交易账号,挂单真实请求返回链路未覆盖;本轮覆盖交易入口页加载和前台恢复基础路径。
|
||||
- 临时 stdin 压测脚本未落盘,覆盖 100000 个通信实例索引查询、100000 行表格 Sendable 延迟合并、1000 次挂单刷新触发合并:
|
||||
- `queueIndexedMs`: 4ms
|
||||
- `tableDeferredMs`: 4ms
|
||||
- `requestRealSendAfter1000Triggers`: 2
|
||||
|
||||
### 新模拟器真实账户验证
|
||||
|
||||
- 设备:Mate X7,`127.0.0.1:5557`,HarmonyOS 6.1.1(24);通过 `devecocli ui` 完成控件树检查和交互。
|
||||
- 真实账户登录成功,交易首页权益、可用资金和行情盘口加载完成。
|
||||
- 进入当日委托/挂单页面,连续 10 轮切换挂单、委托、成交并触发刷新;页面保持响应,无 `APPFREEZE`,无应用 crash。
|
||||
- 该轮未执行下单、撤单或改价,避免产生真实资金业务副作用。
|
||||
- 设备网络不可用时出现 DNS/监控上传错误;同时观察到已有 SQLite 空表名查询错误,均未导致应用退出或冻屏,不纳入本次 TOP 修复结论。
|
||||
- 最后一次误触打开交易时间提醒 WebView,页面仍正常展示;该路径不属于本次交易 SDK 压测范围。
|
||||
|
||||
### Wukong 滑动压力验证
|
||||
|
||||
- 设备:Mate X7,`127.0.0.1:5557`;仅允许 `com.hexin.plat.hmn.futures`,固定种子 `20260821`。
|
||||
- Wukong 随机测试报告:`task status=success`、`task time=149s`、`task count=100`,事件为 100 次滑动;点击、键盘、旋转和应用切换比例均为 0。
|
||||
- Wukong 异常统计为空;设备 `APPFREEZE` 查询为空,应用 crash 查询为空。
|
||||
- 报告目录:`/data/local/tmp/wukong/report/20260821_152309/`。该轮未执行交易点击,真实账户无资金业务副作用。
|
||||
|
||||
### 按修改点专项复验
|
||||
|
||||
- `hq_table`:国内行情大表执行 Wukong 50 次纯滑动,报告 `task status=success`、`task count=50`、滑动事件 50 次、异常为空;页面仍可正常进入合约详情。
|
||||
- Worker 解码:合约详情连续 8 轮切换分时、日 K、1/5/15 分钟,图表和行情数据正常,无 `APPFREEZE` 或 crash。
|
||||
- `GuaDanViewModel` 与 `QueueManagement`:真实账户交易页在设置更新后重新进入当日委托,连续 5 轮切换挂单/委托并刷新;页面正常,无 `APPFREEZE` 或 crash。
|
||||
- `TradeCustomTabManager`:当前真实账户和灰度环境未展示期权/套利独立下单 Tab,仅触发交易设置更新事件并返回交易页验证回退/重建路径;期权/套利 Tab 的直接专项验证待灰度开放后补测。
|
||||
|
||||
## 后续验证
|
||||
|
||||
需要用新包重新采集线上或真机冻屏采样,确认 `TableDataSource`、`QueueManagement`、`isArbitrageEnabled`、`TradeGuaDanListView.onForeground` 不再进入 TOP 耗时函数。短函数如 `ColorUtils.transFormServerColor()`、`StringUtil.isNumber()` 和 `BaseView` 绘制闭包仍应结合完整栈判断,不单独做微调。
|
||||
@@ -0,0 +1,80 @@
|
||||
# 期货 HarmonyOS 客户端稳定性治理需求开发文档
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文将现有冻结(App Freeze)与内存溢出(OOM)整改记录整理为可研发落地、可测试验收的需求。目标是在不改变行情、交易、画线业务协议及用户可见规则的前提下,降低主线程阻塞、请求重复与对象滞留风险。
|
||||
|
||||
本文记录的是已验证改造的需求基线,不代表所有项均已随正式版本发布;版本接入与线上复采必须以各上游仓库和发布包为准。
|
||||
|
||||
## 2. 范围与代码归属
|
||||
|
||||
| 域 | 主要归属 | 本次目标 |
|
||||
| ------ | ------------------------------------------------------------------- | --------------------- |
|
||||
| 应用业务 | `harmony-ths-futures` | 交易、行情与 WebView 生命周期治理 |
|
||||
| 通信 | `ohos_mobile_lib_communication` | 解码、曲线解析、请求队列与缓冲释放 |
|
||||
| 交易 SDK | `harmony_futures_trade_sdk` `futures_trade_sdk`(原生 `lib-weituosdk`) | Native 锁规避、成交推送、市价缓存 |
|
||||
| 画线 SDK | `hmdrawlinebasicsdk` | 绘制候选集去重 |
|
||||
|
||||
本仓库仅保存变更说明,不能直接构建或产出 HAP/HAR。
|
||||
|
||||
## 3. 功能需求
|
||||
|
||||
### FR-01 通信与解析异步化
|
||||
|
||||
1. XML 响应的字节解码和属性解析必须在 TaskPool 中完成;主线程仅消费解析结果。解析入口只允许携带可序列化数据,禁用 DOCTYPE,并保持登录会话、验证码、手机号查询的原有回调与失败语义。
|
||||
2. `HXZip` 解压及曲线的 `points × fields` 解析必须在通信 HAR 的持久 Worker 中执行。响应须按请求顺序串行回调;Worker 异常须拒绝当前等待任务,但不得阻断后续消息。
|
||||
3. `QueueManagement` 必须维护 `instanceId → client/companion` 索引,避免响应分发遍历全部客户端;跨 `frameId` 切换时,必须释放旧请求缓冲、主/伴随 client 映射。
|
||||
|
||||
### FR-02 行情与绘制主线程削峰
|
||||
|
||||
1. 表格实时通知在同一事件循环内仅保留最新一份 `TableData`,已有定时器时不得继续创建闭包;切页或释放时必须取消定时器并清空待通知数据。
|
||||
2. 画线候选集去重必须保持“同对象、本地 ID 相同或远端 ID 相同即重复”的既有语义与插入顺序,复杂度由 O(n²) 降为 O(n)。
|
||||
|
||||
### FR-03 交易链路防重复与锁规避
|
||||
|
||||
1. 前台交易页的在线判断必须读取 `currentAccount.isOnline` 缓存,不得同步调用 Native 在线查询;断网、重登、切账号后的条件单补查行为保持不变。
|
||||
2. 同一账号重登必须复用进行中的 Promise;无实际拦截效果的交易发送前 SDK 状态检查必须移除。
|
||||
3. 挂单查询进行中再次触发时,只保留一次尾随刷新;列表构建中的套利灰度判断按账号缓存,并在设置更新、账号或灰度配置变化时失效。
|
||||
4. 成交推送必须按唯一标识去重。首次加载可全量排序,后续记录必须以稳定二分插入,且不改变成交量、盈亏、账号过滤与展示顺序。
|
||||
5. 市价缓存须按“合约 + 价格类型”更新同一节点;消费后必须用可修改数组操作删除节点,禁止重复累积。
|
||||
|
||||
### FR-04 页面与请求资源释放
|
||||
|
||||
1. `TableRequestClient` 切页、取消订阅或释放时,必须幂等地移除旧队列映射、实时订阅、请求缓冲与待通知数据。
|
||||
2. WebView 总览页销毁时,必须停止三个行情请求并清空 client 与 JS bridge 的双向引用。
|
||||
|
||||
## 4. 影响面
|
||||
|
||||
| 需求域 | 用户/业务入口 | 受影响组件 | 保持不变 | 重点回归 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 通信与解析 | 登录、验证码、行情包与 K 线加载 | XML Parser、通信 Worker、`QueueManagement` | 协议、回调结果、消息顺序 | 损坏 XML、大包、周期连续切换、跨 Frame 请求 |
|
||||
| 行情与画线 | 行情列表刷新、合约详情、K 线绘制 | `TableRequestClient`、画线 SDK | 行情数据、线条样式、命中与层级 | 高频推送、分组切换、滚动、跨周期线合并 |
|
||||
| 交易 | 前后台、账号切换、挂单/委托/成交页 | `TradePage`、`TradeApiManager`、交易 SDK | 下单/撤单、账户过滤、成交汇总与排序规则 | 断网重登、重复刷新、重复推送、真实账户切换 |
|
||||
| 页面生命周期 | WebView 行情总览页退出 | `OverViewEvent`、请求 client、JS bridge | 总览请求参数、前后台恢复行为 | 反复进入/退出总览页、页面销毁后回调 |
|
||||
|
||||
## 5. 非功能要求与边界
|
||||
|
||||
- 所有异步任务仅传递基础类型、`Uint8Array` 或其他可序列化数据;单个 Worker 消息受 16 MB 限制,接近上限时需分块设计。
|
||||
- 不修改网络协议、公开接口、交易下单/撤单规则、行情数据模型或画线样式。
|
||||
- 不将 SDK 对象迁移至 Worker;Native 锁的根治依赖交易 SDK 缩小锁粒度,应用侧仅减少进入锁域的次数。
|
||||
- 临时 HAR 路径、压测代码、计数日志和 API SDK 替换不得进入正式提交。
|
||||
|
||||
## 6. 验收标准
|
||||
|
||||
1. 在上游项目执行 `devecocli build --modules <module>@default` 或对应 HAP 构建成功,且 `devecocli check lint` 无新增问题。
|
||||
2. 覆盖登录/断网重登、前后台、账号切换、挂单/委托/成交刷新、行情分组切换、K 线周期切换及 WebView 总览销毁;无 JS Crash、CppCrash、OOM 或 `APPFREEZE`。
|
||||
3. 成交去重、稳定排序、缓存删除和请求解绑须具备单元或白盒压力测试;测试后不存在旧订阅、旧缓冲、待通知数据或缓存节点残留。
|
||||
4. 新包须在真机或模拟器重新采集冻屏采样。`QueueManagement`、`isArbitrageEnabled` 与 `TradeGuaDanListView.onForeground` 不应继续作为可归因的 TOP 耗时热点;分层采样不得重复相加作为性能收益。
|
||||
|
||||
## 7. 交付与追溯
|
||||
|
||||
每个交付项需提供:所属仓库/分支/commit、HAR 或 HAP 版本与校验值、修改文件清单、构建日志、测试设备与场景、前后指标、未覆盖项及回滚包。详细历史证据见 `harmony-ths-futures-freeze/doc/change-notes/` 和 `harmony-ths-futures-apm-oom/doc/change-notes/`。
|
||||
|
||||
## 8. Change-notes 追溯
|
||||
|
||||
| 需求域 | 改动仓库 | 原始功能改动记录(括号内为改动仓库) |
|
||||
| ------ | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 通信与解析 | `harmony-ths-futures`<br>`ohos_mobile_lib_communication` | <ul><li>[02-xml-taskpool-refactor.md](harmony-ths-futures-freeze/doc/change-notes/02-xml-taskpool-refactor.md)(`harmony-ths-futures`)</li><li>[03-communication-taskpool-decompression.md](harmony-ths-futures-freeze/doc/change-notes/03-communication-taskpool-decompression.md)(`ohos_mobile_lib_communication`、`harmony-ths-futures`)</li><li>[04-curve-parsing-taskpool.md](harmony-ths-futures-freeze/doc/change-notes/04-curve-parsing-taskpool.md)(`ohos_mobile_lib_communication`、`harmony-ths-futures`)</li><li>[14-communication-request-buffer-frame-cleanup.md](harmony-ths-futures-apm-oom/doc/change-notes/14-communication-request-buffer-frame-cleanup.md)(`ohos_mobile_lib_communication`、`harmony-ths-futures`)</li></ul> |
|
||||
| 行情与画线 | `harmony-ths-futures`<br>`hmdrawlinebasicsdk` | <ul><li>[09-drawline-dedup-optimization.md](harmony-ths-futures-freeze/doc/change-notes/09-drawline-dedup-optimization.md)(`hmdrawlinebasicsdk`、`harmony-ths-futures`)</li><li>[10-table-request-client-subscription-cleanup.md](harmony-ths-futures-apm-oom/doc/change-notes/10-table-request-client-subscription-cleanup.md)(`harmony-ths-futures`)</li><li>[11-table-request-client-notification-coalescing.md](harmony-ths-futures-apm-oom/doc/change-notes/11-table-request-client-notification-coalescing.md)(`harmony-ths-futures`)</li></ul> |
|
||||
| 交易 | `harmony-ths-futures`<br>`futures_trade_sdk`(原生 `lib-weituosdk`)<br>`harmony_futures_trade_sdk` | <ul><li>[05-trade-sdk-native-lock-app-mitigation.md](harmony-ths-futures-freeze/doc/change-notes/05-trade-sdk-native-lock-app-mitigation.md)(`harmony-ths-futures`、`futures_trade_sdk` 的原生 `lib-weituosdk`)</li><li>[07-trade-sdk-match-push-optimization.md](harmony-ths-futures-freeze/doc/change-notes/07-trade-sdk-match-push-optimization.md)(`harmony-ths-futures`、`harmony_futures_trade_sdk`)</li><li>[08-trade-page-online-status-cache.md](harmony-ths-futures-freeze/doc/change-notes/08-trade-page-online-status-cache.md)(`harmony-ths-futures`)</li><li>[11-top10-followup-fixes.md](harmony-ths-futures-freeze/doc/change-notes/11-top10-followup-fixes.md)(`harmony-ths-futures`)</li><li>[12-trade-sdk-market-price-cache-cleanup.md](harmony-ths-futures-apm-oom/doc/change-notes/12-trade-sdk-market-price-cache-cleanup.md)(`harmony_futures_trade_sdk`)</li></ul> |
|
||||
| 页面生命周期 | `harmony-ths-futures` | <ul><li>[13-webview-overview-request-cleanup.md](harmony-ths-futures-apm-oom/doc/change-notes/13-webview-overview-request-cleanup.md)(`harmony-ths-futures`)</li></ul> |
|
||||
Reference in New Issue
Block a user