模拟器会杀死不像物理设备那样结束后台任务的应用吗?

问题描述 投票:0回答:1

我正在调试为什么倒计时计时器在后台被杀死。

为了更好地熟悉自己,我制作了一个快速而肮脏的计时器实现。它启动后台任务,启动标准计时器,并将其添加到RunLoop。

[每当倒数秒数改变时,我就会打印出剩余的秒数和操作系统给我的秒数(即UIApplication.shared.backgroundTimeRemaining)。

但是,当我在模拟器中运行此程序,启动计时器并将应用程序置于后台时,计时器可以正常工作,并且一直停下来直到它一直倒数为止。

一些注意事项:

我希望计时器不停止直到完成,即使在后台也是如此。但是,我知道操作系统通常会在后台提供3-5分钟。这是我的问题的出处。如果我在后台只有3-5分钟,那为什么我的计时器基本上可以按需运行呢?模拟器不会在后台与物理设备同时杀死应用程序吗?

此外,我还设置了一个回调,以便在操作系统杀死我的情况时触发(即expirationHandler函数提供的beginBackgroundTask(withName:回调)

对此有任何见识都会有所帮助!这是我的View 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
    }
}

这是输出的一些屏幕截图(一个从倒数开始的屏幕截图,一个从触发expirationHandler触发时的屏幕截图,一个从倒数结束时的屏幕截图:]]

When the count down timer ended

When the count down timer was started

When the expirationHandler was fired

我正在调试为什么倒数计时器在后台被杀死。为了更好地熟悉自己,我制作了一个快速而肮脏的计时器实现。它启动后台任务,启动...

ios swift iphone xcode ios-simulator
1个回答
0
投票

嗯,我知道了!足够愚蠢的是,我应该只在物理设备上尝试过(在发布该问题时才没有)。

© www.soinside.com 2019 - 2024. All rights reserved.