getWebdata 反向调用:大厅 observer 改永久挂钩,对齐 msext 双发行为
之前大厅 / 子游戏 observer 都 lifecycle-tied(viewWillAppear setup /
viewWillDisappear teardown),sub-game 内 overlay backgameData pop 时
只有 sub-game H5 收到 getWebdata(大厅 observer 已 teardown)。这与 msext
"NewRootVC observer 永久 + gameController observer 永久" 的双发行为不一致。
大厅 H5 可能依赖"sub-game 内 overlay 操作触发自身刷新",破坏契约。
修复:
- WebContainerViewController.subGameReturnObserver 改"永久挂钩"
- 加 subGameReturnObserverInstalled 标志,首次 setupExternalSubscriptions
时 install 一次,后续永不解绑(msext NewRootVC.m:593 等价)
- teardownExternalSubscriptions 不再触碰它
- SubGameViewController.subGameReturnObserver 保持 lifecycle-tied
- 子游戏自身 backgameData pop:sub-game 已 teardown → 仅大厅触发(单发,msext 等价)
- 子游戏内 overlay backgameData pop:sub-game.viewWillAppear 重新 setup
→ 大厅 + sub-game 双触发(双发,msext 等价)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
6ef90f9c3f
commit
ce3fbb7106
@@ -342,8 +342,13 @@ public final class SubGameViewController: UIViewController {
|
|||||||
bridge.call("phonestate", data: .string(state), callback: nil)
|
bridge.call("phonestate", data: .string(state), callback: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overlay(Phase 7)pop 回子游戏时反向 callback getWebdata。挂在子游戏栈顶
|
// overlay(Phase 7)pop 回子游戏时反向 callback getWebdata。**lifecycle-tied**:
|
||||||
// 期间生效;overlay push 上来时 viewWillDisappear → teardown 自动解绑,避免双发。
|
// 子游戏栈顶时挂钩,overlay push / 子游戏自身 pop 时由 viewWillDisappear 解绑。
|
||||||
|
// 这一行为与大厅 observer 的"永久挂钩"配合,复现 msext 双观察者行为:
|
||||||
|
// - 子游戏自身 backgameData pop 回大厅:sub-game observer 在 viewWillDisappear
|
||||||
|
// 已解绑 → 仅大厅 observer 触发 → 大厅 H5 收到 getWebdata(msext 单发等价)
|
||||||
|
// - 子游戏内 overlay backgameData pop:sub-game.viewWillAppear 时 observer 重挂
|
||||||
|
// → 大厅 + sub-game 双触发 → 大厅 H5 + sub-game H5 双发收到(msext 双观察者等价)
|
||||||
subGameReturnObserver = NotificationCenter.default.addObserver(
|
subGameReturnObserver = NotificationCenter.default.addObserver(
|
||||||
forName: .subGameDidReturn,
|
forName: .subGameDidReturn,
|
||||||
object: nil,
|
object: nil,
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ public final class WebContainerViewController: UIViewController {
|
|||||||
private let shakeHandler = ShakeHandler()
|
private let shakeHandler = ShakeHandler()
|
||||||
|
|
||||||
// MARK: - .subGameDidReturn 观察者(子游戏 backgameData pop 后回大厅,反向 callback getWebdata)
|
// MARK: - .subGameDidReturn 观察者(子游戏 backgameData pop 后回大厅,反向 callback getWebdata)
|
||||||
|
//
|
||||||
|
// 与 msext NewRootVC.m:593 等价:永久挂钩,viewDidLoad 期间一次性 install,
|
||||||
|
// 不随 viewWillDisappear teardown。详见 setupExternalSubscriptions 注释。
|
||||||
|
|
||||||
private var subGameReturnObserver: NSObjectProtocol?
|
private var subGameReturnObserver: NSObjectProtocol?
|
||||||
|
private var subGameReturnObserverInstalled = false
|
||||||
|
|
||||||
// MARK: - Boot pipeline 内部错误分类
|
// MARK: - Boot pipeline 内部错误分类
|
||||||
//
|
//
|
||||||
@@ -447,9 +451,25 @@ public final class WebContainerViewController: UIViewController {
|
|||||||
bridge.call("appservice", data: .string("2"), callback: nil)
|
bridge.call("appservice", data: .string("2"), callback: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// §3.2 [12] getWebdata — 子游戏 pop 回大厅,反向把 backgameData 入参透传给 H5
|
// §3.2 [12] getWebdata — 反向把 backgameData / 弹层 data 入参透传给大厅 H5。
|
||||||
// (msext NewRootVC 监听 "backgameDatatwo" 通知 → callHandler:"getWebdata" 等价)
|
//
|
||||||
// 大厅栈顶时挂钩,pop 进子游戏时由 viewWillDisappear teardown 解绑,避免双发。
|
// 与 msext NewRootVC.m:593 等价:observer 在 viewDidLoad 期间一次性挂钩,
|
||||||
|
// **永久 listen,不随 viewWillDisappear teardown**。这样:
|
||||||
|
//
|
||||||
|
// 1. 子游戏 backgameData 自己 pop 回大厅:
|
||||||
|
// BackGameDataHandler → AppCoordinator.popSubGame → popViewController
|
||||||
|
// sub-game.viewWillDisappear → sub-game 的 observer 解绑(sub-game 走 lifecycle-tied)
|
||||||
|
// 再 post → 仅大厅 observer 触发 → 大厅 H5 收到 getWebdata(msext 单发等价)
|
||||||
|
//
|
||||||
|
// 2. 子游戏内弹层 backgameData pop(弹层 push 自 sub-game):
|
||||||
|
// OverlayViewController → AppCoordinator.popOverlay → popViewController
|
||||||
|
// overlay.viewWillDisappear → sub-game.viewWillAppear → sub-game observer 重新挂钩
|
||||||
|
// 再 post → 大厅 + sub-game observer 同时触发 → 大厅 H5 + sub-game H5 双发收到
|
||||||
|
// (msext NewRootVC + gameController 双观察者等价行为)
|
||||||
|
//
|
||||||
|
// 用 installed 标志保证多次 viewWillAppear 不重复挂钩(避免重复触发)。
|
||||||
|
if !subGameReturnObserverInstalled {
|
||||||
|
subGameReturnObserverInstalled = true
|
||||||
subGameReturnObserver = NotificationCenter.default.addObserver(
|
subGameReturnObserver = NotificationCenter.default.addObserver(
|
||||||
forName: .subGameDidReturn,
|
forName: .subGameDidReturn,
|
||||||
object: nil,
|
object: nil,
|
||||||
@@ -462,6 +482,7 @@ public final class WebContainerViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func teardownExternalSubscriptions() {
|
private func teardownExternalSubscriptions() {
|
||||||
// 仅解绑 onChange / on{Background,Foreground} 回调,monitor 继续在
|
// 仅解绑 onChange / on{Background,Foreground} 回调,monitor 继续在
|
||||||
@@ -469,15 +490,13 @@ public final class WebContainerViewController: UIViewController {
|
|||||||
// 单例 monitor 全局只有一个 closure 引用,子游戏 setup 时会覆盖;
|
// 单例 monitor 全局只有一个 closure 引用,子游戏 setup 时会覆盖;
|
||||||
// 大厅 viewWillDisappear 时 teardown 释放对当前 bridge/webView 的引用
|
// 大厅 viewWillDisappear 时 teardown 释放对当前 bridge/webView 的引用
|
||||||
// 避免双发。
|
// 避免双发。
|
||||||
|
//
|
||||||
|
// subGameReturnObserver 是永久挂钩(与 msext NewRootVC observer 等价),
|
||||||
|
// **不在此 teardown** —— 详见 setupExternalSubscriptions 注释。
|
||||||
BatteryMonitor.shared.onChange = nil
|
BatteryMonitor.shared.onChange = nil
|
||||||
NetworkMonitor.shared.onChange = nil
|
NetworkMonitor.shared.onChange = nil
|
||||||
AppLifecycleObserver.shared.onBackground = nil
|
AppLifecycleObserver.shared.onBackground = nil
|
||||||
AppLifecycleObserver.shared.onForeground = nil
|
AppLifecycleObserver.shared.onForeground = nil
|
||||||
|
|
||||||
if let token = subGameReturnObserver {
|
|
||||||
NotificationCenter.default.removeObserver(token)
|
|
||||||
subGameReturnObserver = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Alerts (terminal / retry states)
|
// MARK: - Alerts (terminal / retry states)
|
||||||
|
|||||||
Reference in New Issue
Block a user