Phase 2.B:3 项反向 callback 落地(getBattery / getnetwork / appservice)
完成大厅业务期实时状态推送给 H5 的事件驱动链路。每次变化时同时(1)
重写对应 app_*.js 文件供下次 reload,(2)bridge.call 反向推送给当前
页 H5 — 双轨与原 msext 行为等价。
新增 Source/Resource/BatteryMonitor.swift:
- @MainActor public final class BatteryMonitor,shared 单例 + 幂等 start()
- 监听 UIDevice.batteryLevelDidChangeNotification + 启动期读首次值
- currentLevel 同步快照(max(level, 0) 兜底模拟器 -1)+ onChange 回调
- notification block 内 MainActor.assumeIsolated 安全跨到 main actor
新增 Source/Resource/AppLifecycleObserver.swift:
- @MainActor public final class AppLifecycleObserver,shared 单例 + 幂等 start()
- 监听 didEnterBackgroundNotification + willEnterForegroundNotification
- onBackground / onForeground 双钩子;assumeIsolated 同上
WebContainerViewController.writeAppDataFiles 接入(替换 inline addObserver):
- 3 个 monitor 启动统一聚合(NetworkMonitor + BatteryMonitor + AppLifecycle)
- BatteryMonitor.onChange:writeBattery + bridge.call("getBattery", "%.2f")
- NetworkMonitor.onChange:writeNetwork + bridge.call("getnetwork", "1"/"2"/"3")
- AppLifecycle.onBackground/Foreground:bridge.call("appservice", "1"/"2")
- 首次值改读 BatteryMonitor.currentLevel 而非 UIDevice 原始值
至此 Phase 2.B 全部完成(5 项反向 callback:getphoneinfo / getBattery /
getnetwork / appservice / shakeEnd 全部接好)。
剩 Phase 2.C ExternalSubscriptions 生命周期管理(防双发,栈深 ≥ 2 时
下层不发桥事件)。
BuildProject 通过
Plan §5 Phase 2.B 全部勾选
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d38ff0f8c4
commit
23b3a1fdad
+10
-10
@@ -359,16 +359,16 @@ Contract Design Plan(本文档)
|
||||
|
||||
##### 2.B Native→H5 反向 callback
|
||||
|
||||
- [ ] **2.8** `Source/Device/DeviceInfo.swift`:6 字段 snapshot
|
||||
- `getphoneInfo` 收到调用后 → `bridge.call("getphoneinfo", data: ...)` 反向
|
||||
- [ ] **2.9** `Source/Device/BatteryMonitor.swift`:监听 `UIDeviceBatteryLevelDidChangeNotification`
|
||||
- 触发 → `bridge.call("getBattery", data: .string("%.2f"))`
|
||||
- [ ] **2.10** `Source/Device/NetworkMonitor.swift`:`NWPathMonitor` 包装
|
||||
- 状态变化 → `bridge.call("getnetwork", data: .string("1"/"2"/"3"))`
|
||||
- [ ] **2.11** `Source/Device/AppLifecycleObserver.swift`:监听 SceneDelegate 前后台通知
|
||||
- → `bridge.call("appservice", data: .string("1"=后台 / "2"=前台))`
|
||||
- [ ] **2.12** `Source/WebView/ShakeDetector.swift`:在 `WebContainerViewController` 重写 `motionEnded:`
|
||||
- → `bridge.call("shakeEnd", data: nil)`(仅 `canshake=YES` 时触发)
|
||||
- [x] **2.8** `Source/Bridge/Handlers/DeviceInfoHandler.swift`:DeviceInfoSnapshot 6 字段(Phase 2.5 一并完成)
|
||||
- `getphoneInfo` 收到 → `bridge.call("getphoneinfo", data: 表 A 6 字段)` 反向
|
||||
- [x] **2.9** `Source/Resource/BatteryMonitor.swift`:监听 `UIDevice.batteryLevelDidChangeNotification`
|
||||
- 触发 → 重写 `app_battery.js` + `bridge.call("getBattery", data: .string("%.2f", level))`
|
||||
- [x] **2.10** `Source/Resource/NetworkMonitor.swift`:NWPathMonitor 包装(Phase 1 已建)
|
||||
- 状态变化 → 重写 `app_network.js` + `bridge.call("getnetwork", data: .string("1"/"2"/"3"))`
|
||||
- [x] **2.11** `Source/Resource/AppLifecycleObserver.swift`:监听 `UIApplication.didEnterBackgroundNotification` / `willEnterForegroundNotification`
|
||||
- 后台 → `bridge.call("appservice", data: .string("1"))`;前台 → `"2"`
|
||||
- [x] **2.12** ShakeDetector 集成在 `Source/Bridge/Handlers/ShakeHandler.swift`(Phase 2.3 一并完成)
|
||||
- `WebContainerViewController.motionEnded(_:with:)` 转发 → `bridge.call("shakeEnd", data: nil)`(仅 `canShake=YES` 时触发)
|
||||
|
||||
##### 2.C 外部订阅生命周期
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// AppLifecycleObserver.swift
|
||||
// ylgamehall
|
||||
//
|
||||
// 监听 UIApplication 前/后台切换通知,提供 onBackground / onForeground 钩子。
|
||||
//
|
||||
// 契约:docs/H5-Native-Contract.md §3.2 [4]appservice 反向 callback
|
||||
// "1" = 进入后台 / "2" = 回到前台(命名错位沿用历史)
|
||||
// Phase 2.11
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
public final class AppLifecycleObserver {
|
||||
|
||||
public static let shared = AppLifecycleObserver()
|
||||
|
||||
public var onBackground: (@MainActor () -> Void)?
|
||||
public var onForeground: (@MainActor () -> Void)?
|
||||
|
||||
private var started = false
|
||||
|
||||
public init() {}
|
||||
|
||||
public func start() {
|
||||
guard !started else { return }
|
||||
started = true
|
||||
let nc = NotificationCenter.default
|
||||
nc.addObserver(
|
||||
forName: UIApplication.didEnterBackgroundNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
MainActor.assumeIsolated {
|
||||
self?.onBackground?()
|
||||
}
|
||||
}
|
||||
nc.addObserver(
|
||||
forName: UIApplication.willEnterForegroundNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
MainActor.assumeIsolated {
|
||||
self?.onForeground?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// BatteryMonitor.swift
|
||||
// ylgamehall
|
||||
//
|
||||
// UIDevice.batteryLevelDidChangeNotification 的最简 wrap:
|
||||
// 提供 currentLevel 同步快照 + onChange 主线程回调。
|
||||
//
|
||||
// 契约:docs/H5-Native-Contract.md §3.2 [2]getBattery 反向 callback
|
||||
// Phase 2.9
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
public final class BatteryMonitor {
|
||||
|
||||
public static let shared = BatteryMonitor()
|
||||
|
||||
/// 当前电量 0.0…1.0;模拟器无电池硬件返回 -1,BatteryMonitor.start() 后兜底为 0
|
||||
public private(set) var currentLevel: Float = 0
|
||||
|
||||
/// 电量变化回调(主线程触发)
|
||||
public var onChange: (@MainActor (Float) -> Void)?
|
||||
|
||||
private var started = false
|
||||
|
||||
public init() {}
|
||||
|
||||
public func start() {
|
||||
guard !started else { return }
|
||||
started = true
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
currentLevel = max(UIDevice.current.batteryLevel, 0)
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.batteryLevelDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
// forName: + queue: .main 的 block 闭包在 main 队列触发;
|
||||
// 用 MainActor.assumeIsolated 把它桥到 main actor 隔离
|
||||
MainActor.assumeIsolated {
|
||||
guard let self else { return }
|
||||
let level = max(UIDevice.current.batteryLevel, 0)
|
||||
self.currentLevel = level
|
||||
self.onChange?(level)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,8 +216,10 @@ public final class WebContainerViewController: UIViewController {
|
||||
)
|
||||
}
|
||||
|
||||
/// 写 4 个 app_*.js 文件,并挂钩 battery / network 变化的重写监听。
|
||||
/// 与原 msext NewRootVC.initJSdata + changebattery + changenetstate 等价。
|
||||
/// 写 4 个 app_*.js 文件,并挂钩 battery / network / appservice 三组钩子:
|
||||
/// 每次变化时同时(1)重写对应 .js 文件(下次 reload 读到新值)
|
||||
/// 和(2)`bridge.call` 反向推送给当前页 H5(§3.2 [2][3][4])。
|
||||
/// 与原 msext NewRootVC.initJSdata + changebattery + changenetstate + appservice 等价。
|
||||
private func writeAppDataFiles(resolved: ResolvedVersion) throws {
|
||||
let writer = AppDataWriter(
|
||||
bundleConfig: .shared,
|
||||
@@ -225,28 +227,43 @@ public final class WebContainerViewController: UIViewController {
|
||||
containerRole: .lobby
|
||||
)
|
||||
|
||||
// 启动 NetworkMonitor(幂等:内部 started 标记)
|
||||
// 启动 3 个 monitor(幂等:内部 started 标记)
|
||||
NetworkMonitor.shared.start()
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
BatteryMonitor.shared.start()
|
||||
AppLifecycleObserver.shared.start()
|
||||
|
||||
// 首次写入:4 个文件全部落盘
|
||||
try writer.writeInitial()
|
||||
try writer.writeBattery(UIDevice.current.batteryLevel)
|
||||
try writer.writeBattery(BatteryMonitor.shared.currentLevel)
|
||||
try writer.writeNetwork(NetworkMonitor.shared.currentCode)
|
||||
|
||||
// 持续监听:battery / network 变化时重写对应文件。
|
||||
// 注意:H5 此次加载已读到首次写入的值,下次 reload 才会读到新值;
|
||||
// 业务期间的实时变化由 §3.2 [2][3]反向 callback 推送(Phase 2 实现)。
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.batteryLevelDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { _ in
|
||||
try? writer.writeBattery(UIDevice.current.batteryLevel)
|
||||
let bridge = bridgedWebView.bridge
|
||||
|
||||
// §3.2 [2] getBattery — UIDevice 电量变化
|
||||
// data: 字符串 "%.2f" 形式的小数电量
|
||||
BatteryMonitor.shared.onChange = { level in
|
||||
try? writer.writeBattery(level)
|
||||
bridge.call("getBattery",
|
||||
data: .string(String(format: "%.2f", level)),
|
||||
callback: nil)
|
||||
}
|
||||
|
||||
// §3.2 [3] getnetwork — NWPath 变化
|
||||
// data: 字符串 "1"=无网 / "2"=WiFi / "3"=蜂窝
|
||||
NetworkMonitor.shared.onChange = { code in
|
||||
try? writer.writeNetwork(code)
|
||||
bridge.call("getnetwork",
|
||||
data: .string("\(code)"),
|
||||
callback: nil)
|
||||
}
|
||||
|
||||
// §3.2 [4] appservice — App 前后台切换(命名错位沿用历史)
|
||||
// data: "1"=进入后台 / "2"=回到前台
|
||||
AppLifecycleObserver.shared.onBackground = {
|
||||
bridge.call("appservice", data: .string("1"), callback: nil)
|
||||
}
|
||||
AppLifecycleObserver.shared.onForeground = {
|
||||
bridge.call("appservice", data: .string("2"), callback: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user