feat(background-task): 实现原生与JS层的任务同步机制

解决后台任务在JS监听器未就绪时丢失的问题。新增任务缓存队列,当检测到无JS监听器时将任务暂存,并启动20秒超时计时器等待JS初始化完成。JS层通过markJSReady接口通知原生层准备就绪,触发缓存任务的立即执行。超时后自动切换到默认处理逻辑,确保任务不丢失。
This commit is contained in:
richarjiang
2025-11-06 09:20:52 +08:00
parent ea22901553
commit 9b1a40cea3
3 changed files with 97 additions and 12 deletions

View File

@@ -28,4 +28,7 @@ RCT_EXTERN_METHOD(backgroundRefreshStatus:(RCTPromiseResolveBlock)resolver
RCT_EXTERN_METHOD(simulateLaunch:(RCTPromiseResolveBlock)resolver RCT_EXTERN_METHOD(simulateLaunch:(RCTPromiseResolveBlock)resolver
rejecter:(RCTPromiseRejectBlock)rejecter) rejecter:(RCTPromiseRejectBlock)rejecter)
RCT_EXTERN_METHOD(markJSReady:(RCTPromiseResolveBlock)resolver
rejecter:(RCTPromiseRejectBlock)rejecter)
@end @end

View File

@@ -27,6 +27,10 @@ class BackgroundTaskBridge: RCTEventEmitter {
private var requiresExternalPower = false private var requiresExternalPower = false
private var defaultDelay: TimeInterval = 60 * 30 // 30 minutes private var defaultDelay: TimeInterval = 60 * 30 // 30 minutes
private var currentTask: BGTask? private var currentTask: BGTask?
private let pendingTaskWaitTimeout: TimeInterval = 20
private var waitingForJSListeners = false
private var pendingTaskPayload: [String: Any]?
private var pendingTaskTimeoutWorkItem: DispatchWorkItem?
override init() { override init() {
super.init() super.init()
@@ -49,6 +53,9 @@ class BackgroundTaskBridge: RCTEventEmitter {
override func startObserving() { override func startObserving() {
hasListeners = true hasListeners = true
queue.async { [weak self] in
self?.emitPendingTaskIfPossible()
}
} }
override func stopObserving() { override func stopObserving() {
@@ -345,6 +352,22 @@ class BackgroundTaskBridge: RCTEventEmitter {
#endif #endif
} }
@objc
func markJSReady(
_ resolver: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
queue.async { [weak self] in
guard let self else { return }
let hadPendingTask = self.waitingForJSListeners && self.pendingTaskPayload != nil
self.emitPendingTaskIfPossible()
resolver([
"pendingTaskFlushed": hadPendingTask
])
}
}
// MARK: - Private helpers // MARK: - Private helpers
@available(iOS 13.0, *) @available(iOS 13.0, *)
@@ -408,6 +431,11 @@ class BackgroundTaskBridge: RCTEventEmitter {
self.currentTask = task self.currentTask = task
NSLog("[BackgroundTaskBridge] 开始处理后台任务: \(task.identifier)") NSLog("[BackgroundTaskBridge] 开始处理后台任务: \(task.identifier)")
if self.identifier == nil {
self.identifier = task.identifier
NSLog("[BackgroundTaskBridge] 使用任务标识符初始化 identifier: \(task.identifier)")
}
// //
task.expirationHandler = { [weak self] in task.expirationHandler = { [weak self] in
guard let self else { return } guard let self else { return }
@@ -435,28 +463,29 @@ class BackgroundTaskBridge: RCTEventEmitter {
} }
} }
let payload: [String: Any] = [
"identifier": self.identifier ?? task.identifier,
"timestamp": Date().timeIntervalSince1970 * 1000
]
guard self.hasListeners else { guard self.hasListeners else {
NSLog("[BackgroundTaskBridge] 没有JS监听器执行默认处理并重新调度") NSLog("[BackgroundTaskBridge] 暂无 JS 监听器,等待 JS 初始化 (最多 \(Int(self.pendingTaskWaitTimeout)) 秒)")
// 使JS self.cacheTaskForLater(payload: payload)
self.executeDefaultTaskAndReschedule()
return return
} }
NSLog("[BackgroundTaskBridge] 发送后台任务执行事件到JS") NSLog("[BackgroundTaskBridge] 发送后台任务执行事件到JS")
DispatchQueue.main.async { self.emitTaskToJS(payload: payload)
self.sendEvent(
withName: "BackgroundTaskBridge.execute",
body: [
"identifier": self.identifier ?? "",
"timestamp": Date().timeIntervalSince1970 * 1000
]
)
}
} }
} }
@available(iOS 13.0, *) @available(iOS 13.0, *)
private func executeDefaultTaskAndReschedule() { private func executeDefaultTaskAndReschedule() {
waitingForJSListeners = false
pendingTaskPayload = nil
pendingTaskTimeoutWorkItem?.cancel()
pendingTaskTimeoutWorkItem = nil
// JS // JS
NSLog("[BackgroundTaskBridge] 执行默认后台任务逻辑") NSLog("[BackgroundTaskBridge] 执行默认后台任务逻辑")
@@ -506,4 +535,48 @@ class BackgroundTaskBridge: RCTEventEmitter {
} }
} }
} }
private func cacheTaskForLater(payload: [String: Any]) {
waitingForJSListeners = true
pendingTaskPayload = payload
pendingTaskTimeoutWorkItem?.cancel()
let timeoutItem = DispatchWorkItem { [weak self] in
guard let self else { return }
guard self.waitingForJSListeners else { return }
NSLog("[BackgroundTaskBridge] 等待 JS 监听器超时,执行默认处理")
self.waitingForJSListeners = false
self.pendingTaskPayload = nil
self.pendingTaskTimeoutWorkItem = nil
self.executeDefaultTaskAndReschedule()
}
pendingTaskTimeoutWorkItem = timeoutItem
queue.asyncAfter(deadline: .now() + pendingTaskWaitTimeout, execute: timeoutItem)
}
private func emitPendingTaskIfPossible() {
guard hasListeners else { return }
guard waitingForJSListeners, let payload = pendingTaskPayload else { return }
pendingTaskTimeoutWorkItem?.cancel()
pendingTaskTimeoutWorkItem = nil
waitingForJSListeners = false
pendingTaskPayload = nil
NSLog("[BackgroundTaskBridge] JS 监听器已就绪,发送缓存的后台任务事件")
emitTaskToJS(payload: payload)
}
private func emitTaskToJS(payload: [String: Any]) {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.sendEvent(
withName: "BackgroundTaskBridge.execute",
body: payload
)
}
}
} }

View File

@@ -363,6 +363,15 @@ export class BackgroundTaskManagerV2 {
this.handleTaskExpiration(); this.handleTaskExpiration();
}); });
if (typeof NativeBackgroundModule.markJSReady === 'function') {
try {
await NativeBackgroundModule.markJSReady();
logger.info('[BackgroundTaskManagerV2] 已通知原生层 JS 监听器就绪');
} catch (readyError) {
logger.warn('[BackgroundTaskManagerV2] 通知原生层 JS 准备状态失败', readyError);
}
}
logger.info('[BackgroundTaskManagerV2] 事件监听器注册完成'); logger.info('[BackgroundTaskManagerV2] 事件监听器注册完成');
try { try {