背景
App后台存活时间长短直接对用户体验、业务功能、用户卸载几个方面都有较大的影响。如果存活时间过短,用户每次退到后台后都需要重启App,对于用户来说都是非常不友好的,非常有可能导致用户卸载App,这将是非常大的损失。因此我们着重调研了影响App后台存活时间的具体因素。经过调研发现,除了设备性能影响,后台任务是一个比较重要的方面,大家可能平时并没有重点关注过后台任务对后台存活时间的影响,对后台任务相关的内容并不了解。为了搞清楚后台任务对存活时间的影响和后台任务的使用场景笔者做了深入的调研,一方面是为了解决上面的两个问题,另一方面也是为了对这块的知识做储备。下面笔者将主要介绍一下后台任务使用时存在的坑,以及我们是如何模拟此类场景的。希望这篇文章结能够对你有帮助。
后台任务介绍
什么是后台任务
正常情况下,当App进入后台,会立即变为挂起状态,App中的任何执行都会停止。但是由于需求原因我们并不希望进入后台后马上停止我们的App中的操作,而是让我们的操作执行完成后在变为挂起状态。此时就用到了后台任务,开启后台任务后系统会继续让你的App保持活跃状态,直到任务结束后变为挂起状态。因此通过一句话总结就是,后台任务是系统提供给的让App在后台短暂保持活跃状态的功能。
后台任务与后台常驻App的区别
后台任务只是短暂的让App在后台保持活跃状态,任务结束后App会变为挂起状态,不用额外的权限申请。而后台常驻App是在后台一直保持活跃状态,例如音频类App、地图类App,即使进入后台,App一致是活跃状态。此类型App属于特殊类型,需要申请特殊权限。
后台任务使用
正确开启后台任务
- api介绍
//Marks the start of a task that should continue if the app enters the background.
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler;
}
//Marks the end of a specific long-running background task.
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier;
- 任务开启和结束
UIApplication *app;
UIBackgroundTaskIdentifier wbTaskID;
- (void)WBStartTask{
bgTaskID = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"wb__==: %lu", (unsigned long)wbTaskID);
[app endBackgroundTask:wbTaskID];
}];
}
- (void)WBEndTask{
[app endBackgroundTask:wbTaskID];
}
- 注意事项
beginBackgroundTaskWithExpirationHandler:、endBackgroundTask:必须成对出现,否则会触发存后台任务泄漏。
后台任务的执行时间
不同版本的系统后台任务执行时间也不一样,明细如下:
系统 | 理论时间(s) | 实测时间(s) |
---|---|---|
<= iOS6 | 600 | - |
iOS7 - iOS12 | 180 | 176 |
>= iOS13 | 30 | 26 |
以上时间为打点测试,测试的是任务开始到结束的时间,并不是App由活跃到挂起的时间,挂起时间可以通过xcode控制台进行测试,这里就不详细介绍了。
后台任务对App后台存活时间的影响
错误的使用后台任务都会导致App在后台被系统强杀。主要包括两个方面:后台任务超时和后台任务泄漏。后台任务超时是后台任务规定时间内没有完成相关的耗时操作并且没有结束任务,导致超时;而后台任务泄露是由于开启了后台任务,当任务执行完成后没有将任务结束导致的。因此如果不合理的使用后台任务,App大概率会被系统杀死,大大降低了App在后台的存活时间。
后台任务超时
什么时后台任务超时
一句话总结就是在后台任务中执行耗时操作,任务执行结束后耗时操作仍然在执行。
什么场景会导致后台任务超时
苹果已经给出了任务超时不同场景,笔者对场景进行了模拟,下面是笔者通过demo触发任务超时后的crash日志,通过code码进行了归类。
- 0xbada5e47 开启的后台任务超过阈值,目前阈值为1000个,如果开启的后台任务超过1000,进入后台后会被系统强杀。
复现场景:开启大于1000个后台任务,demo验证后日志如下:
Incident Identifier: 58790B23-E0E9-4650-AC5E-4023A662C7BE
CrashReporter Key: 1610ccc74368b31360a22139adc06b185565627b
Hardware Model: iPhone8,1
Process: OOMTestProjcet [63403]
Path: /private/var/containers/Bundle/Application/11D48709-CDFB-4A60-A730-FA84E8CFB989/OOMTestProjcet.app/OOMTestProjcet
Identifier: wmm.OOMTestProjcet
Version: 1 (1.0)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: wmm.OOMTestProjcet [6715]
Date/Time: 2019-08-29 11:02:07.5557 +0800
Launch Time: 2019-08-29 11:01:47.5072 +0800
OS Version: iPhone OS 11.2.1 (15C153)
Baseband Version: 4.30.02
Report Version: 104
Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace ASSERTIOND, Code 0xbada5e47
Triggered by Thread: 0
Filtered syslog:
None found
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001824c17c4 kevent_id + 8
1 libdispatch.dylib 0x0000000182346498 _dispatch_kq_poll + 208
2 libdispatch.dylib 0x0000000182346e88 _dispatch_event_loop_wait_for_ownership$VARIANT$mp + 432
3 libdispatch.dylib 0x0000000182338b44 _dispatch_sync_wait + 416
4 AssertionServices 0x0000000184eeac54 -[BKSAssertion invalidate] + 84
5 UIKit 0x000000018bedbfc4