From ac4cb3b0f99c9209237a11fa81204596ce3c961b Mon Sep 17 00:00:00 2001 From: ozh Date: Wed, 9 Sep 2026 18:39:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(ble):=20=E8=AE=BE=E5=A4=87=E5=88=97?= =?UTF-8?q?=E8=A1=A8=20UI=20=E5=88=B7=E6=96=B0=E5=8A=A0=20300ms=20?= =?UTF-8?q?=E8=8A=82=E6=B5=81=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=90=9C=E5=88=B0?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=90=8E=E7=82=B9=E5=87=BB=E5=8D=A1=E6=AD=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 搜索开启 allowDuplicatesKey=true,目标设备广播包每秒到达数次且 RSSI 逐包变化——upsertDevice 的签名判重只拦"完全相同"的包,挡不住信号 强度变化。每包都会走 setDeviceMap → onDeviceListChange → React setState → 页面重渲染,搜到设备后以每秒数十次的频率打满渲染线程, 点击事件被排队到数百毫秒后处理,表现为"搜到设备就无法停搜索"。 - setDeviceMap 改用 emitDeviceListThrottled:数据实时合入 deviceMap, 通知 UI 压到每 300ms 一次 - resetDeviceMap/stopSearch 绕过节流立即冲刷:清空与最终帧不能延迟 - cleanupDiscovery 补清节流定时器 --- src/ble/discovery.js | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/ble/discovery.js b/src/ble/discovery.js index eee35fc..54c2ed9 100644 --- a/src/ble/discovery.js +++ b/src/ble/discovery.js @@ -15,6 +15,9 @@ const FILTER_SCAN_TIMEOUT_MS = 4000 const RESTART_SCAN_DELAY_MS = 120 const DISCOVERY_POLL_INTERVAL_MS = 1200 const SCAN_DURATION_MS = 30000 +// 设备列表 UI 刷新最小间隔:搜索中 allowDuplicatesKey=true,目标设备每秒广播 +// 数次,不节流会以每秒数十次 setState 打满小程序渲染线程,点击被排队卡死 +const DEVICE_LIST_EMIT_INTERVAL_MS = 300 const TARGET_NAME_KEYWORDS = Object.freeze(['ED713', '713WQ', 'ED719', 'EP832']) function promisifyTaroApi(apiName, params = {}) { @@ -80,6 +83,7 @@ export function createBluetoothDiscovery(options = {}) { let filterFallbackTimer = null let discoveryPollTimer = null let autoStopTimer = null + let deviceListEmitTimer = null let offDeviceFound = null let offAdapterState = null @@ -116,14 +120,41 @@ export function createBluetoothDiscovery(options = {}) { onDeviceListChange(getSortedDeviceList()) } + // 广播包持续到达时(RSSI 每包都变,签名判重挡不住),节流 UI 刷新; + // 数据仍实时合入 deviceMap,只是通知 UI 的频率被压到每 300ms 一次。 + // 末包到达后补一次尾部刷新,保证最后一帧数据不丢 + function emitDeviceListThrottled() { + if (deviceListEmitTimer) { + return + } + + deviceListEmitTimer = setTimeout(() => { + deviceListEmitTimer = null + emitDeviceList() + }, DEVICE_LIST_EMIT_INTERVAL_MS) + } + + function clearDeviceListEmitTimer() { + if (!deviceListEmitTimer) { + return + } + + clearTimeout(deviceListEmitTimer) + deviceListEmitTimer = null + } + function setDeviceMap(nextMap) { deviceMap = nextMap onDeviceMapChange({...deviceMap}) - emitDeviceList() + emitDeviceListThrottled() } function resetDeviceMap() { - setDeviceMap({}) + clearDeviceListEmitTimer() + deviceMap = {} + onDeviceMapChange({...deviceMap}) + // 重置列表必须立即通知:清空操作不能等 300ms 节流窗口 + emitDeviceList() } function normalizeUuid(uuid) { @@ -687,6 +718,10 @@ export function createBluetoothDiscovery(options = {}) { } await stopDiscoverySilently() + + // 停搜后立即冲刷最后一帧列表,避免 pending 的节流定时器延迟刷新 + clearDeviceListEmitTimer() + emitDeviceList() } function pickPreferredDeviceId(deviceId) { @@ -874,6 +909,7 @@ export function createBluetoothDiscovery(options = {}) { function cleanupDiscovery() { clearFallbackTimer() clearDiscoveryPollTimer() + clearDeviceListEmitTimer() searchSessionId += 1 acceptingDeviceEvents = false