gpt4 book ai didi

ios - WKWatchConnectivityRefreshBackgroundTask 与 WCSessionDelegate 竞争

转载 作者:行者123 更新时间:2023-11-29 00:40:01 30 4
gpt4 key购买 nike

我正在尝试调整我的代码,从仅在前台使用 WCSessionDelegate 回调到在后台通过 handleBackgroundTasks: 接受 WKWatchConnectivityRefreshBackgroundTask。文档指出后台任务可能会异步进入,并且在 WCSessionhasContentPendingNO< 之前不应调用 setTaskCompleted/.

如果我将我的 watch 应用置于后台并从 iPhone 应用transferUserInfo:,我能够成功接收到我的第一个WKWatchConnectivityRefreshBackgroundTask。但是,hasContentPending 始终是 YES,因此我保存了该任务并简单地从我的 WCSessionDelegate 方法返回。如果我再次 transferUserInfo:hasContentPendingNO,但没有与此消息关联的 WKWatchConnectivityRefreshBackgroundTask。也就是说,后续的 transferUserInfo: 不会触发对 handleBackgroundTask: 的调用——它们只是由 WCSessionDelegate 处理。即使我立即 setTaskCompleted 而不检查 hasContentPending,后续的 transferUserInfo: 也由 session:didReceiveUserInfo: 处理,我不需要再次激活 WCSession

我不确定在这里做什么。似乎没有办法强制 WCSession 停用,并且遵循有关延迟 setTaskCompleted 的文档似乎让我在操作系统方面遇到了麻烦。

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

我看过 QuickSwitch 2.0源代码(修复 Swift 3 错误后,rdar://28503030),他们的方法似乎根本不起作用(有关此的 another SO thread)。我已经尝试将 KVO 用于 WCSessionhasContentPendingactivationState,但仍然没有任何 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

最佳答案

根据您的描述和我的理解,这听起来好像工作正常。

向我解释的方式是 watchOS 上的新 handleBackgroundTasks: 旨在成为以下用途的一种方式:

  • 与 WatchKit 扩展通信的系统,了解其在后台启动/恢复的原因,以及
  • WatchKit 扩展的一种方式,可以让系统知道它何时完成了它想要做的工作,因此可以再次终止/挂起。

这意味着每当 watch 上收到传入的 WatchConnectivity 有效负载并且您的 WatchKit 扩展被终止或暂停时,您应该会收到一个 handleBackgroundTasks: 回调,让您知道为什么在后台运行。这意味着您可以接收 1 个 WKWatchConnectivityRefreshBackgroundTask 但多个 WatchConnectivity 回调(文件、userInfos、applicationContext)。 hasContentPending 让您知道您的 WCSession 已交付所有初始的待处理内容(文件、userInfos、applicationContext)。此时,您应该对 WKWatchConnectivityRefreshBackgroundTask 对象调用 setTaskCompleted。

然后,您可以预期您的 WatchKit 扩展将很快被挂起或终止,除非您收到其他 handleBackgroundTasks: 回调并因此有其他 WK 后台任务对象需要完成。

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

关于ios - WKWatchConnectivityRefreshBackgroundTask 与 WCSessionDelegate 竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39735723/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com