如果用户关闭已经在后台的应用程序会发生啥?
Posted
技术标签:
【中文标题】如果用户关闭已经在后台的应用程序会发生啥?【英文标题】:What happens if the user closes an app that is already in the background?如果用户关闭已经在后台的应用程序会发生什么? 【发布时间】:2013-04-18 17:09:55 【问题描述】:在后台任务执行期间,如果用户杀死应用程序(已经处于后台模式)会发生什么?
想象一下:
应用启动任务 X(有 10 分钟的后台支持和必须调用的过期处理程序)。 然后,应用程序进入后台,用户终止应用程序。
我对应用程序被终止后任务 X 会发生什么感到困惑。它还有执行的后台时间吗?过期处理程序是否被调用过?
【问题讨论】:
我不知道为什么这会被否决,但是自己测试它不是很简单吗? 有趣的问题。文档对此尚不清楚。尝试一下。让我们知道实际发生了什么。我很好奇在这种情况下是否调用了applicationWillTerminate
UIApplicationDelegate
方法。
【参考方案1】:
如果应用“已经在后台”,则用户已经“关闭了应用”!那么你的问题意味着什么?你已经进入了后台,如果你打电话给beginBackgroundTaskWithExpirationHandler:
,一切都会正常进行。
你的意思是用户在后台强行杀死应用程序,通过召唤“最近的应用程序”界面进入jiggy模式并从“最近的应用程序”界面删除应用程序?然后该应用程序被立即杀死;你没有收到任何通知,你正在做的任何事情都会被打断。
此外,到期处理程序块应该做的唯一事情是调用endBackgroundTask:
。如果您被立即杀死,那么您无法拨打此电话的事实并不重要!
【讨论】:
你明白了。但是调用expirationHandler很重要,我有代码必须执行。 那么你错误地设计了过期处理程序。过期处理程序应该做的就是调用endBackgroundTask:
。过期处理程序意味着你的后台任务时间已经过期;除了承认这一点并离开之外,您没有时间做任何事情。如果你不这样做,你的应用程序不会被暂停 - 它会被杀死。
这里的另一个担忧是“我有代码......必须执行”。没有任何保证,对不起。您的架构需要接受这一点。
正如马特所说,您的架构必须接受它。如果设备关闭、坠毁或掉入湖中,您会怎么做?未来的代码执行永远无法确定。 SIGKILL
只是可能发生在您身上的众多事情之一。
哦哦哦我明白了!!!所以好吧,我确实误解了过期处理程序的全部内容! (无论如何......为什么系统不能让我的应用程序使用的 backgroundtask id 无效?)感谢 matt 和 Rob Napier!【参考方案2】:
好的,这就是结果
在这种情况下,操作系统会向您的应用进程发送 SIGKILL 信号,并且不会调用 applicationWillTerminate 方法。
以下只是我对 Apple 文档、猜测工作和 Google 结果的解释。
在这种情况下,您的应用程序委托的以下方法将被调用
- (void)applicationWillTerminate:(UIApplication *)application
引用自 Apple 文档
对于不支持后台执行的应用程序或 与 ios 3.x 或更早版本相关联时,始终调用此方法 用户退出应用程序。对于支持的应用程序 后台执行,一般不调用这个方法 用户退出应用程序,因为应用程序只是移动到 在那种情况下的背景。 但是,这个方法可能会被调用 应用程序在后台运行的情况(不是 暂停),并且系统出于某种原因需要终止它。
因此,您必须在 plist 文件中将 UIApplicationExitsOnSuspend 值设置为 YES,否则无法保证 applicationWillTerminate: 将被调用。这就是为什么文档中使用了可能。
虽然我不确定,但我认为不会调用过期处理程序块。
【讨论】:
确认了吗?该方法的文档含糊不清。很明显,如果您的应用支持正确的UIBackgroundModes
之一,则调用此委托方法。但尚不清楚如果您的应用在后台被用户杀死,而应用位于beginBackgroundTaskWithExpirationHandler
中,是否会调用此委托。
文档中的引用是我提到的不清楚的信息。对我来说,这些文档不清楚在这种情况下是否调用了代表。有人需要真正尝试一下 - 任何接受者?我要出去一会儿,否则我会试一试。
我认为不能保证系统会调用 applicationWillTerminate
接收SIGKILL
的应用程序立即死亡。它不会也不能运行任何额外的代码。它无法捕捉到信号。它不能忽略信号。它被毫不客气地扔了。 UIKit(更不用说你的代码)没有机会做任何事情,因为它发生在操作系统层。
请注意,这实际上并不含糊。对SIGKILL
的响应是几十年前POSIX 定义的一部分。 UIKit 仅记录其控制范围内的行为,SIGKILL
发生在其控制范围之外。如果您需要更多详细信息,可以查看sigaction(2)
手册页。 developer.apple.com/library/ios/#documentation/system/…【参考方案3】:
这很容易测试,所以我刚刚(在运行 iOS 6.1.3 的 iPhone 4S 上)使用了我将在最后粘贴的代码,它在应用程序委托的 applicationDidEnterBackground
方法中启动了一个后台任务。
结果令人惊讶。
当用户通过单击主页按钮退出应用程序,然后手动终止应用程序(通过双击主页,将事物置于 jiggy 模式并点击应用程序的关闭图标)时,会发生以下情况:
applicationWillTerminate
被调用。
当applicationWillTerminate
退出时,后台执行将终止,无论后台任务还剩多少执行时间。该应用已被终止。
但是..
如果您安排事情以使applicationWillTerminate
在被调用后不退出,如下面的代码所示,当应用程序被手动终止时,会发生以下情况——至少在我的测试设置中是这样:
-
应用的后台任务继续在后台运行。
即使分配的后台执行时间到期,后台任务也会继续运行,就像
applicationWillTerminate
中的代码一样,直到该方法退出。
这显然是一个错误——你不应该永远继续运行代码——而且我不会依赖它一直工作。但是那些一直在使用各种技巧来在后台播放音频以保持应用程序存活的人可能想要进行调查。如果其他人在不同的 iOS 版本/设备上尝试代码并获得相同的结果,我会很感兴趣。
我的测试项目中 AppDelegate.m 的代码:
//
// BTAppDelegate.m
// BackgroundTest
//
// Created by David Fearon on 07/05/2013.
// Copyright (c) 2013 David Fearon. All rights reserved.
//
#import "BTAppDelegate.h"
@implementation BTAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSLog(@"application didFinishLaunchingWithOptions called");
// Override point for customization after application launch.
return YES;
- (void)applicationWillResignActive:(UIApplication *)application
NSLog(@"applicationWillResignActive: called");
- (void)applicationDidEnterBackground:(UIApplication *)application
NSLog(@"applicationDidEnterBackground: called");
UIApplication* thisApp = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier __block task = [thisApp beginBackgroundTaskWithExpirationHandler:^
];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
[self printTimeRemaining];
while(YES)
[NSThread sleepForTimeInterval:1.0];
[self printTimeRemaining];
//[thisApp endBackgroundTask:task];
);
-(void)printTimeRemaining
NSLog(@"Background task time remaining: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
- (void)applicationWillEnterForeground:(UIApplication *)application
NSLog(@"applicationWillEnterForeground: called");
- (void)applicationDidBecomeActive:(UIApplication *)application
NSLog(@"applicationDidBecomeActive: called");
- (void)applicationWillTerminate:(UIApplication *)application
NSLog(@"applicationWillTerminate: called");
while(YES)
[NSThread sleepForTimeInterval:1.0];
NSLog(@"Still executing code in applicationWillTerminate.");
@end
【讨论】:
苹果应用商店审核团队可能会进行测试来检查?以上是关于如果用户关闭已经在后台的应用程序会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章
如果我将 UIKit 应用程序更新为 SwiftUI,老 iOS 用户会发生啥?
java中如果在关闭Socket时发生一个I/O错误,则会抛出一个啥异常?