[英]Does the Simulator kill off apps that don't end background tasks just like a physical device does?
I'm working on debugging why a count down timer is being killed off in the background.我正在调试为什么倒计时计时器在后台被杀死。
To familiarize myself better, I made a quick and dirty timer implementation.为了更好地熟悉自己,我做了一个快速而肮脏的计时器实现。 It starts a background task, starts a standard timer, and adds it to the RunLoop.
它启动一个后台任务,启动一个标准计时器,并将其添加到 RunLoop。
Whenever the seconds change on the count down, I print out how many seconds on the count down I have left and how many seconds the OS has given me (ie UIApplication.shared.backgroundTimeRemaining
).每当倒计时的秒数发生变化时,我都会打印出我还剩多少秒以及操作系统给了我多少秒(即
UIApplication.shared.backgroundTimeRemaining
)。
However, when I run this in the simulator, start the timer, and put the app in the background, the timer works just fine and never stops until it counted all the way down.但是,当我在模拟器中运行它,启动计时器并将应用程序置于后台时,计时器工作得很好并且永远不会停止,直到它一直倒计时。
Some things to keep in mind:需要记住的一些事项:
I WANT the timer to not stop until done, even if in background.我希望计时器在完成之前不会停止,即使在后台也是如此。 However, I know the OS typically gives 3-5 mins in the background.
但是,我知道操作系统通常会在后台提供 3-5 分钟。 This is where my question comes from.
这就是我的问题的来源。 If I only have 3-5 mins in the background, then why is my timer running basically as long as it needs to?
如果我在后台只有 3-5 分钟,那为什么我的计时器基本上只要它需要的时间就运行? Does the Simulator not kill off apps in the same time as the physical device in the background?
模拟器不会在后台关闭物理设备的同时关闭应用程序吗?
Additionally, I also set a callback to fire when and if the OS kills my off (ie the expirationHandler
callback provided by the beginBackgroundTask(withName:
function)此外,我还设置了一个回调以在操作系统杀死我时触发(即
beginBackgroundTask(withName:
function) 提供的expirationHandler
回调
Any insight into this would be helpful: Here's my View Controller class:对此的任何见解都会有所帮助:这是我的视图 Controller 类:
class TimerViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
// MARK: - Outlets
@IBOutlet weak var timeLeftLabel: UILabel!
@IBOutlet weak var timePicker: UIPickerView!
// MARK: - Properties
var timer: Timer?
var timeLeft: Int = 0 {
didSet {
DispatchQueue.main.async {
self.timeLeftLabel.text = "\(self.timeLeft.description) seconds left"
}
}
}
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
let backgroundTaskName = "bgTask"
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
@objc func applicationDidMoveToBackground() {
print("moved to backgorund")
}
@objc func applicationWillMoveToForegraund() {
print("moved to foreground")
}
// MARK: - Setup
func setupUI() {
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidMoveToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillMoveToForegraund), name: UIApplication.willEnterForegroundNotification, object: nil)
timePicker.tintColor = .white
timePicker.backgroundColor = .clear
}
func registerBackgroundTask() {
//end any bg tasks
endBackgroundTask()
//start new one
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: backgroundTaskName, expirationHandler: {
//times up, do this stuff when ios kills me
print("background task being ended by expiration handler")
self.endBackgroundTask()
})
assert(backgroundTask != UIBackgroundTaskIdentifier.invalid)
//actual meat of bg task
print("starting")
timePicker.isHidden = true
timeLeftLabel.isHidden = false
timeLeft = getCurrentPickerViewSeconds()
timeLeftLabel.text = "\(timeLeft) seconds left"
setupTimer()
}
func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(self.backgroundTask)
self.backgroundTask = .invalid
}
func setupTimer() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fire), userInfo: nil, repeats: true)
if timer != nil {
RunLoop.current.add(timer!, forMode: .common)
} else {
print("timer is nil, didnt add to runloop")
}
}
// MARK: - Helpers
func getCurrentPickerViewSeconds() -> Int {
let mins = timePicker.selectedRow(inComponent: 0)
let seconds = timePicker.selectedRow(inComponent: 1)
let totalSeconds = seconds + (mins * 60)
return totalSeconds
}
// MARK: - Actions
@objc func fire() {
print("current time left: \(timeLeft)")
print("background time remaining: \(UIApplication.shared.backgroundTimeRemaining)")
if timeLeft > 0 {
timeLeft -= 1
} else {
print("done")
stopTimer()
}
}
@IBAction func startTimer() {
registerBackgroundTask()
}
@IBAction func stopTimer() {
print("stopping")
endBackgroundTask()
timePicker.isHidden = false
timeLeftLabel.isHidden = true
timer?.invalidate()
timer = nil
}
@IBAction func resetTimer() {
print("resetting")
stopTimer()
startTimer()
}
@IBAction func doneTapped() {
print("done-ing")
stopTimer()
dismiss(animated: true, completion: nil)
}
// MARK: - Picker View
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 59
}
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
var label = ""
switch component {
case 0:
label = "m"
case 1:
label = "s"
default:
label = ""
}
let result = "\(row) \(label)"
let attributedResult = NSAttributedString(string: result, attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
return attributedResult
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let seconds = row + (60 * component)
timeLeft = seconds
}
}
Here's are some screenshots of the output (one from the start of the count down, one from when the expirationHandler was fired, one from the end of the count down):下面是 output 的一些截图(一张从倒计时开始,一张从 expireHandler 被触发时,一张从倒计时结束):
Well, I figured it out, Stupidly enough.好吧,我想通了,够愚蠢的。 I should have just tried it on a physical device (just didn't have one at the time of posting that question).
我应该只是在物理设备上尝试过(只是在发布该问题时没有)。
Findings:发现:
The simulator does NOT behave in the same way as the physical device because it does NOT kill off apps after the allowed time from the OS is up (ie the UIApplication.shared.backgroundTimeRemaining is basically a lie/non issue when in the simulator).模拟器的行为方式与物理设备不同,因为它不会在操作系统允许的时间结束后终止应用程序(即,在模拟器中时,UIApplication.shared.backgroundTimeRemaining 基本上是一个谎言/非问题)。
On the other hand, with a physical device, as soon as backgroundTimeRemaining hit 0, the app was killled off which was expected.另一方面,对于物理设备,一旦 backgroundTimeRemaining 达到 0,应用程序就会被终止,这是意料之中的。 Here's a screenshot of the output:
这是output的截图:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.