我尝试调整我的代码,使其仅在前台使用WCSessionDelegate
个回调,在后台通过WKWatchConnectivityRefreshBackgroundTask
接受handleBackgroundTasks:
。文档指出后台任务可能是异步进行的,并且在setTaskCompleted
WCSession
hasContentPending
为NO
之前,不应该调用transferUserInfo:
。
如果我将我的手表应用程序放入后台并从iPhone应用程序WKWatchConnectivityRefreshBackgroundTask
,我就能够成功收到我的第一个hasContentPending
。但是,YES
始终为WCSessionDelegate
,因此我将保存任务,只需从transferUserInfo:
方法返回即可。如果我再次hasContentPending
,则NO
为WKWatchConnectivityRefreshBackgroundTask
,但此消息中没有transferUserInfo:
。也就是说,后续handleBackgroundTask:
不会触发对WCSessionDelegate
的调用 - 它们只是由setTaskCompleted
处理。即使我立即hasContentPending
而没有检查transferUserInfo:
,后续session:didReceiveUserInfo:
也由WCSession
处理,而我无需再次激活WCSession
。
我不知道该怎么做。似乎没有办法迫使setTaskCompleted
停用,并且遵循有关延迟WKExtensionDelegate
的文档似乎让我遇到操作系统问题。
我已发布并记录了一个示例项目,该项目说明了GitHub上的工作流程,并粘贴了下面的WCSession
代码。我是否做错了选择或在某条线上错误地解释了文档?
我查看了QuickSwitch 2.0源代码(在修复了Swift 3错误之后,rdar:// 28503030),他们的方法似乎无法正常工作(其中' s another SO thread关于此事)。我已尝试将{KVO'用于hasContentPending
activationState
和WKWatchConnectivityRefreshBackgroundTask
,但仍然没有任何#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"
答案 0 :(得分:1)
根据您的描述和我的理解,听起来这是正常的。
向我解释的方式是watchOS上的新handleBackgroundTasks:
旨在成为:
这意味着每当Watch上收到传入的WatchConnectivity有效负载并且您的WatchKit扩展程序被终止或暂停时,您应该期待一个handleBackgroundTasks:
回调,让您知道您在后台运行的原因。这意味着您可以收到1个WKWatchConnectivityRefreshBackgroundTask
但有几个WatchConnectivity回调(文件,userInfos,applicationContext)。 hasContentPending
可让您知道WCSession
何时传递了所有初始待处理内容(文件,userInfos,applicationContext)。此时,您应该在WKWatchConnectivityRefreshBackgroundTask
对象上调用setTaskCompleted。
然后,您可以预期此后不久将暂停或终止您的WatchKit扩展,除非您已收到其他handleBackgroundTasks:
回调,因此需要完成其他WK后台任务对象。
我发现当使用调试器附加到进程时,操作系统可能不像通常那样暂停它们,因此它建议使用日志记录检查此行为,如果您想确保避免任何这些有点问题。