From 97749ac177194e8a973507402d437fc2ecea0ed7 Mon Sep 17 00:00:00 2001 From: joywayer Date: Wed, 24 Jun 2026 01:50:04 +0800 Subject: [PATCH] =?UTF-8?q?3.E=20=E4=BF=AE=E5=A4=8D=EF=BC=9A=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E5=90=AF=E5=8A=A8=E6=9D=83=E9=99=90=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=B8=8E=E6=B5=AE=E5=B1=82=20touchObserver=20=E6=AD=BB?= =?UTF-8?q?=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 首次启动 app,H5 按住麦克风按钮触发 prepareaudio: 1. 主动调 AVAudioSession.requestRecordPermission 弹系统权限对话框 2. 用户为了点"允许"必须松手 3. 松手被 RecordingAwareWindow.touchObserver 立刻捕获 → recorder.stop() 4. 浮层瞬间关闭,录音失败 5. 用户感受:"按一次没反应,再点也没用" ## 根因 我之前用 `requestRecordPermission` 主动请求权限(会同步弹系统对话框), 与 daoqi `FuncPublic.ifauth`(只读 `AVCaptureDevice.authorizationStatus`, 不主动请求)行为不一致。主动请求 + 按住状态 + touchObserver 全局监听 三者形成死锁。 ## 修复 改用只读状态 + 三分支处理: - `.denied / .restricted`:弹 Alert + return(对齐 daoqi) - `.notDetermined`(首次):**主动弹权限对话框但不启动录音浮层**。 用户做完选择后 return,不触发 cb。第二次按录音按钮时走 .authorized 分支正常录音 - `.authorized`:直接启动录音浮层 ## 与 daoqi 行为对比 daoqi 在 .notDetermined 时 ifauth 返回 1(继续启动录音),但实测此路径 也有体验问题——浮层弹出 + 系统对话框遮挡 + 用户松手立刻触发 touchEnded → 录音空文件 → 转码失败 → silent return。最终用户体验也是"按一次没 反应"。 新外壳的修复让首次行为更明确(不启动录音 = 不会有空文件上传),与 daoqi 真实终态(不触发 getaudiourl)等价,UX 等价(首次启动需按两次)。 ## H5 契约影响 无。"不触发 cb" 与 daoqi 转码失败 silent return 等价;H5 收不到 getaudiourl 反向 callback 时本来就要兼容(daoqi 各类失败路径都不触发 cb)。 ## 验证 BuildProject 通过 0 错误。 --- .../Bridge/Handlers/RemoteAudioHandler.swift | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift b/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift index ac0605e..124dc07 100644 --- a/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift +++ b/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift @@ -85,16 +85,39 @@ public enum RemoteAudioHandler { isSubGame: Bool, callback: BridgeCallback? ) async { - // 1. 麦克风权限请求 - let granted: Bool = await withCheckedContinuation { cont in - AVAudioSession.sharedInstance().requestRecordPermission { ok in - cont.resume(returning: ok) - } - } - guard granted else { - // 拒绝:弹 Alert + 不触发 callback(对齐 daoqi NewRootVC.m:314-319) + // 1. 麦克风权限**只读**状态检查(对齐 daoqi FuncPublic.m:1144-1170 ifauth 实现)。 + // 主动请求权限(requestRecordPermission)会立即弹出 iOS 系统对话框; + // 在 H5 按住状态下弹系统对话框 → 用户必须松手点"允许" → + // RecordingAwareWindow.touchObserver 捕获松手立刻 stop → 浮层瞬间关闭, + // 用户体验异常(首次启动 app 录音永远失败)。 + let status = AVCaptureDevice.authorizationStatus(for: .audio) + switch status { + case .denied, .restricted: + // 明确拒绝 → 弹 Alert + 不触发 cb(对齐 daoqi NewRootVC.m:314-319) showMicrophonePermissionAlert(on: hostVC()) return + + case .notDetermined: + // 首次启动:弹系统权限对话框,**不**启动录音浮层(避免与 touchObserver + // 死锁)。用户做完选择 / 第二次按录音按钮时走 .authorized 分支正常录音。 + // + // ⚠️ daoqi ifauth 在 .notDetermined 时返回 1(继续录音),但 daoqi 用 + // NotificationCenter 全局监听,行为可能与新外壳的 touchObserver 不一致; + // 经实测新外壳走 daoqi 老路径会"按一次没反应"——改用先请求权限再 return + // 的策略,等价于 "第一次按录音按钮 = 触发权限请求;第二次按 = 正常录音"。 + _ = await withCheckedContinuation { cont in + AVAudioSession.sharedInstance().requestRecordPermission { granted in + cont.resume(returning: granted) + } + } + // 无论用户允许/拒绝都不触发 cb(H5 端会因没收到 cb 知道本次录音未发生) + return + + case .authorized: + break // 继续启动录音 + + @unknown default: + return } // 2. 启动录音浮层