WKWatchConnectivityRefreshBackgroundTask与WCSessionDelegate竞争

时间:2016-09-27 23:14:36

标签: ios watchkit watch-os watchconnectivity

我尝试调整我的代码,使其仅在前台使用WCSessionDelegate个回调,在后台通过WKWatchConnectivityRefreshBackgroundTask接受handleBackgroundTasks:。文档指出后台任务可能是异步进行的,并且在setTaskCompleted WCSession hasContentPendingNO之前,不应该调用transferUserInfo:

如果我将我的手表应用程序放入后台并从iPhone应用程序WKWatchConnectivityRefreshBackgroundTask,我就能够成功收到我的第一个hasContentPending。但是,YES始终为WCSessionDelegate,因此我将保存任务,只需从transferUserInfo:方法返回即可。如果我再次hasContentPending,则NOWKWatchConnectivityRefreshBackgroundTask,但此消息中没有transferUserInfo:。也就是说,后续handleBackgroundTask:不会触发对WCSessionDelegate的调用 - 它们只是由setTaskCompleted处理。即使我立即hasContentPending而没有检查transferUserInfo:,后续session:didReceiveUserInfo:也由WCSession处理,而我无需再次激活WCSession

我不知道该怎么做。似乎没有办法迫使setTaskCompleted停用,并且遵循有关延迟WKExtensionDelegate的文档似乎让我遇到操作系统问题。

我已发布并记录了一个示例项目,该项目说明了GitHub上的工作流程,并粘贴了下面的WCSession代码。我是否做错了选择或在某条线上错误地解释了文档?

我查看了QuickSwitch 2.0源代码(在修复了Swift 3错误之后,rdar:// 28503030),他们的方法似乎无法正常工作(其中&#39; s another SO thread关于此事)。我已尝试将{KVO'用于hasContentPending activationStateWKWatchConnectivityRefreshBackgroundTask,但仍然没有任何#import "ExtensionDelegate.h" @interface ExtensionDelegate() @property (nonatomic, strong) WCSession *session; @property (nonatomic, strong) NSMutableArray<WKWatchConnectivityRefreshBackgroundTask *> *watchConnectivityTasks; @end @implementation ExtensionDelegate #pragma mark - Actions - (void)handleBackgroundTasks:(NSSet<WKRefreshBackgroundTask *> *)backgroundTasks { NSLog(@"Watch app woke up for background task"); for (WKRefreshBackgroundTask *task in backgroundTasks) { if ([task isKindOfClass:[WKWatchConnectivityRefreshBackgroundTask class]]) { [self handleBackgroundWatchConnectivityTask:(WKWatchConnectivityRefreshBackgroundTask *)task]; } else { NSLog(@"Handling an unsupported type of background task"); [task setTaskCompleted]; } } } - (void)handleBackgroundWatchConnectivityTask:(WKWatchConnectivityRefreshBackgroundTask *)task { NSLog(@"Handling WatchConnectivity background task"); if (self.watchConnectivityTasks == nil) self.watchConnectivityTasks = [NSMutableArray new]; [self.watchConnectivityTasks addObject:task]; if (self.session.activationState != WCSessionActivationStateActivated) [self.session activateSession]; } #pragma mark - Properties - (WCSession *)session { NSAssert([WCSession isSupported], @"WatchConnectivity is not supported"); if (_session != nil) return (_session); _session = [WCSession defaultSession]; _session.delegate = self; return (_session); } #pragma mark - WCSessionDelegate - (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(NSError *)error { switch(activationState) { case WCSessionActivationStateActivated: NSLog(@"WatchConnectivity session activation changed to \"activated\""); break; case WCSessionActivationStateInactive: NSLog(@"WatchConnectivity session activation changed to \"inactive\""); break; case WCSessionActivationStateNotActivated: NSLog(@"WatchConnectivity session activation changed to \"NOT activated\""); break; } } - (void)sessionWatchStateDidChange:(WCSession *)session { switch(session.activationState) { case WCSessionActivationStateActivated: NSLog(@"WatchConnectivity session activation changed to \"activated\""); break; case WCSessionActivationStateInactive: NSLog(@"WatchConnectivity session activation changed to \"inactive\""); break; case WCSessionActivationStateNotActivated: NSLog(@"WatchConnectivity session activation changed to \"NOT activated\""); break; } } - (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo { /* * NOTE: * Even if this method only sets the task to be completed, the default * WatchConnectivity session delegate still picks up the message * without another call to handleBackgroundTasks: */ NSLog(@"Received message with counter value = %@", userInfo[@"counter"]); if (session.hasContentPending) { NSLog(@"Task not completed. More content pending..."); } else { NSLog(@"No pending content. Marking all tasks (%ld tasks) as complete.", (unsigned long)self.watchConnectivityTasks.count); for (WKWatchConnectivityRefreshBackgroundTask *task in self.watchConnectivityTasks) [task setTaskCompleted]; [self.watchConnectivityTasks removeAllObjects]; } } @end 完成,这使得感觉到我目前对这个问题的解释。

"2011-04-11T10:20:30Z"

1 个答案:

答案 0 :(得分:1)

根据您的描述和我的理解,听起来这是正常的。

向我解释的方式是watchOS上的新handleBackgroundTasks:旨在成为:

的方式
  • 系统与WatchKit扩展程序进行通信,以便在后台启动/恢复它,以及
  • WatchKit扩展程序的一种方式,让系统知道它何时完成了它想要做的工作,因此可以再次终止/暂停。

这意味着每当Watch上收到传入的WatchConnectivity有效负载并且您的WatchKit扩展程序被终止或暂停时,您应该期待一个handleBackgroundTasks:回调,让您知道您在后台运行的原因。这意味着您可以收到1个WKWatchConnectivityRefreshBackgroundTask但有几个WatchConnectivity回调(文件,userInfos,applicationContext)。 hasContentPending可让您知道WCSession何时传递了所有初始待处理内容(文件,userInfos,applicationContext)。此时,您应该在WKWatchConnectivityRefreshBackgroundTask对象上调用setTaskCompleted。

然后,您可以预期此后不久将暂停或终止您的WatchKit扩展,除非您已收到其他handleBackgroundTasks:回调,因此需要完成其他WK后台任务对象。

我发现当使用调试器附加到进程时,操作系统可能不像通常那样暂停它们,因此它建议使用日志记录检查此行为,如果您想确保避免任何这些有点问题。