setKeepAliveTimeout 和后台任务
我对这个话题很头疼。我正在开发一个需要定期轮询网络服务器以检查新数据的应用程序。根据返回的信息,我希望向用户推送本地通知。
我知道这种方法与 Apple 描述的方法略有不同,在 Apple 中,远程服务器基于 APNS 进行工作,推送远程通知。然而,有很多原因我不能考虑这种方法。其中最重要的是用户身份验证机制。出于安全原因,远程服务器无法考虑用户凭据。我所能做的就是将登录和获取核心移至客户端(iPhone)。
我注意到 Apple 为应用程序提供了唤醒并保持打开的 Socket 连接(即 VoIP 应用程序)的机会。
于是,我就这样开始了调查。在 plist 中添加了所需的信息,我可以在 appDelegate 中使用类似的内容来“唤醒”我的应用程序:
[[UIApplication sharedApplication] setKeepAliveTimeout:1200 handler:^{
NSLog(@"startingKeepAliveTimeout");
[self contentViewLog:@"startingKeepAliveTimeout"];
MyPushOperation *op = [[MyPushOperation alloc] initWithNotificationFlag:0 andDataSource:nil];
[queue addOperation:op];
[op release];
}];
NSOperation,然后使用以下块代码启动后台任务:
#pragma mark SyncRequests
-(void) main {
NSLog(@"startSyncRequest");
[self contentViewLog:@"startSyncRequest"];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"exipiration handler triggered");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self cancel];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableURLRequest *anURLRequest;
NSURLResponse *outResponse;
NSError *exitError;
NSString *username;
NSString *password;
NSLog(@"FirstLogin");
anURLRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:webserverLogin, username, password]]];
[anURLRequest setHTTPMethod:@"GET"];
[anURLRequest setTimeoutInterval:120.00];
[anURLRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
exitError = nil;
NSData *tmpData = [NSURLConnection sendSynchronousRequest:anURLRequest returningResponse:&outResponse error:&exitError];
[anURLRequest setTimeoutInterval:120.00];
if(exitError != nil) { //somethings goes wrong
NSLog(@"somethings goes wrong");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self cancel];
return;
}
//do some stuff with NSData and prompt the user with a UILocalNotification
NSLog(@"AlltasksCompleted");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self cancel];
});
}
}
上面的代码似乎可以工作(有时) ,但许多其他它使我的应用程序崩溃,并显示以下日志信息:
Exception Type: 00000020
Exception Codes: 0x8badf00d
Highlighted Thread: 3
Application Specific Information:
DemoBackApp[5977] has active assertions beyond permitted time:
{(
<SBProcessAssertion: 0xa9da0b0> identifier: UIKitBackgroundCompletionTask process: DemoBackApp[5977] permittedBackgroundDuration: 600.000000 reason: finishTask owner pid:5977 preventSuspend preventIdleSleep
)}
Elapsed total CPU time (seconds): 0.010 (user 0.010, system 0.000), 100% CPU
Elapsed application CPU time (seconds): 0.000, 0% CPU
对于那些询问的人,是的。我也尝试过 Async NSURLConnection 方法。不管。即使我使用带有超时处理程序和 didFinishLoading:WithError 的异步方法,它也会崩溃。
我被困住了。任何提示都受到高度赞赏。
I've a big headache with the topic. I'm working on an application that needs to poll a webserver regularly, in order to check for new data. Based on the returned information, I wish to push a local notification to the user.
I know that this approach is slightly different from the one depicted by Apple, in which a remote server makes the work, pushing a remote notification, based on APNS. However, there are many reasons for which i cannot take this approach in consideration. One for all, is the user authentication mechanism. The remote server, for security reasons, cannot take into account the user credentials. All that i can do is to move the login and fetching core, to the client (iPhone).
I noticed that Apple offers an opportunity for applications to wake-up and keep opened a Socket connection (ie. a VoIP application).
So, I started investigate in this way. Added the required information in the plist, I'm able to "wake" my application, using something like this in my appDelegate:
[[UIApplication sharedApplication] setKeepAliveTimeout:1200 handler:^{
NSLog(@"startingKeepAliveTimeout");
[self contentViewLog:@"startingKeepAliveTimeout"];
MyPushOperation *op = [[MyPushOperation alloc] initWithNotificationFlag:0 andDataSource:nil];
[queue addOperation:op];
[op release];
}];
The NSOperation, then starts a background task using the following block code:
#pragma mark SyncRequests
-(void) main {
NSLog(@"startSyncRequest");
[self contentViewLog:@"startSyncRequest"];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"exipiration handler triggered");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self cancel];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableURLRequest *anURLRequest;
NSURLResponse *outResponse;
NSError *exitError;
NSString *username;
NSString *password;
NSLog(@"FirstLogin");
anURLRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:webserverLogin, username, password]]];
[anURLRequest setHTTPMethod:@"GET"];
[anURLRequest setTimeoutInterval:120.00];
[anURLRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
exitError = nil;
NSData *tmpData = [NSURLConnection sendSynchronousRequest:anURLRequest returningResponse:&outResponse error:&exitError];
[anURLRequest setTimeoutInterval:120.00];
if(exitError != nil) { //somethings goes wrong
NSLog(@"somethings goes wrong");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self cancel];
return;
}
//do some stuff with NSData and prompt the user with a UILocalNotification
NSLog(@"AlltasksCompleted");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self cancel];
});
}
}
The above code seems to work (sometimes), but many other it crashes my application, with the following log information:
Exception Type: 00000020
Exception Codes: 0x8badf00d
Highlighted Thread: 3
Application Specific Information:
DemoBackApp[5977] has active assertions beyond permitted time:
{(
<SBProcessAssertion: 0xa9da0b0> identifier: UIKitBackgroundCompletionTask process: DemoBackApp[5977] permittedBackgroundDuration: 600.000000 reason: finishTask owner pid:5977 preventSuspend preventIdleSleep
)}
Elapsed total CPU time (seconds): 0.010 (user 0.010, system 0.000), 100% CPU
Elapsed application CPU time (seconds): 0.000, 0% CPU
For ones who ask, yes. I've tried the Async NSURLConnection approach, too. No matter. It crash the same, even if I use an async approach with timeout handler and didFinishLoading:WithError.
I'm stuck. Any hints are high appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是一个旧线程,但可能需要更新。
从 iOS 6 开始,这是我在 VoIP 计时器后台方法中看到的行为,如本线程中所讨论的:
希望有帮助!
This is an old thread, but may warrant an update.
As of iOS 6, this is the behavior I am seeing with the VoIP timer background methods as discussed in this thread:
Hope that helps!
当您调用
-setKeepAliveTimeout:handler:
时,您最多只有 30 秒的时间来完成所有操作并挂起。您不会获得与应用程序首次转换到后台时相同的后台宽限期。这意味着完成长时间运行的任务、关闭设备等。使用 VOIP 回调,您只需发送需要发送到服务的任何 ping 数据包,以保持网络连接处于活动状态且不会超时。 30 秒后,无论启动新的后台任务,如果您的应用程序仍在执行,您将被终止。
另外,请务必注意,如果您实际上不是 VOIP 应用程序或者,如果您在 VOIP 回调窗口期间执行任何与保持网络连接打开无关的操作,您的应用 >将被应用商店拒绝。当您设置任何保持活动标志(VOIP、背景音乐、导航)时,他们会对其进行非常严格的测试,以确保它仅在后台执行标记为要做的事情。执行任何类型的 HTTP GET 请求并等待一些大数据更新返回几乎肯定会导致您的应用程序被拒绝。
编辑:正如 Patrick 在评论中指出的那样,iOS 5 中当前块执行的时间已从 30 秒减少到 10 秒。每当重新链接时,最好留意这些时间如果您的应用程序需要新版本的 SDK,请务必至少快速检查文档是否已更新(随着 iOS 6 的推出,此数字可能会再次调整)。
When you call
-setKeepAliveTimeout:handler:
, you only get a maximum of 30 seconds to complete everything and suspend. You're not given the same background grace period as you'd be given when your application is first transitioned to the background. That's meant for finishing up long running tasks, shutting things down, etc.With the VOIP callback, you're just supposed to send whatever ping packet you need to send to your service to keep the network connection alive and not timed-out. After 30 seconds, regardless of starting new background tasks, if your application is still executing, you're gonna be terminated.
Also, it's important to note that if you're not actually a VOIP application or if you do anything during the VOIP callback window that's not related to keeping your network connections open, your app will be rejected from the app store. When you set any of the stay-active flags (VOIP, background music, navigation), they test it pretty rigorously to ensure that it only does what it's flagged to do while in the background. Doing any kind of HTTP GET request and waiting for some large data update to come back is almost guaranteed to get your app rejected.
EDIT: As noted by Patrick in the comments, the current amount of time the block is given to execute has been reduced from 30 seconds to 10 seconds with iOS 5. It's a good idea to keep an eye on these times whenever you re-link your application for a new version of the SDK, always at least quickly check the docs in case they've been updated (with iOS 6 coming out, this number may be tweaked again).
只是为了更新 iOS7 的此行为。
由backgroundTimeRemaining 报告的任务时间。然而,它停止了
根据报告,在此时间用完之前 5 秒做出响应
背景时间剩余。
前台秒数,后跟 60 秒后台计时器:
由backgroundTimeRemaining 报告。根据backgroundTimeRemaining 的报告,它还会在该时间用完之前的 5 秒处停止响应。
所以在iOS7上你每10分钟可以获得65秒的处理时间。
Just to update this behaviour for iOS7.
task time as reported by backgroundTimeRemaining. However, it stops
responding at 5 seconds before this time runs out as reported by
backgroundTimeRemaining.
seconds of foreground followed by 60 seconds of background timer as
reported by backgroundTimeRemaining. It also stops responding at 5 seconds before this time runs out as reported by backgroundTimeRemaining.
So on iOS7 you can get 65 seconds of processing time every 10 minutes.
看来您可以通过在被调用时附带有限后台任务执行的请求来组合保持活动超时处理程序。每次调用 VOIP 保持活动处理程序时,这将允许您有整整 10 分钟的时间(而不是通常的 10-30 秒)。
仍然存在与上面相同的问题 - 您需要在 plist 中添加 VOIP 标志才能提交,如果您有该标志并且实际上不是 VOIP 应用程序,而是用于内部分发(企业或否则),这个解决方案应该可以很好地为您提供后台时间。
在我的测试中,每次调用 VOIP 处理程序时都会重置 10 分钟有限执行计时器(无论用户是否从那时起将应用程序置于前台),这应该意味着您可以在 VOIP 处理程序中无限期地轮询服务器。每 600 秒(10 分钟)一次后台运行,轮询过程在进入睡眠状态之前可能需要长达 10 分钟的时间(如果您需要的话,这意味着几乎持续的后台操作)。
再说一次,除非你能让他们相信你是 VOIP,否则这并不是 App Store 的真正选择。
It seems that you may be able to combine the keep alive timeout handler by piggy-backing a request for finite background task execution when you get called. This will allow you a full 10 minutes every time the VOIP keep alive handler gets called (vice the usual 10-30 seconds).
Still same problem as above -- in that you need the VOIP flag in your plist to submit, and Apple isn't likely to accept your application if you have that flag and are not actually a VOIP application, but for internal distribution (enterprise or otherwise), this solution should work fine for giving you background time.
In my tests, the 10 minute finite execution timer is reset every time the VOIP handler is called (whether or not the user has brought the application to the front since then), and this should mean that you could poll your server indefinitely while in the background once every 600 seconds (10 minutes) and the polling process can take up to 10 minutes before going to sleep (meaning nearly constant background operation if that is what you require).
Again, not really an option for the App Store unless you can convince them you are VOIP.