【问题标题】:Lags in UI when using a Timer使用计时器时 UI 滞后
【发布时间】:2018-08-27 03:44:53
【问题描述】:

我正在使用每秒重复一次的计时器来更新正在播放的currentPlaybackTime,因此我每秒都会根据当前歌曲的进度更新我的标签。 我曾经使用 NSTimer 或在 Swift 中使用 Timer 每秒重复一次,但是在更改歌曲或开始和停止歌曲时遇到问题,秒标签会滞后(它会保持 5 秒不变) ,当您听到歌曲播放然后会更新到正确的时间)。 然后我继续使用 GCD,我遇到了同样的问题,即使在后台队列中也是如此。然后我去使用 DispatchSourceTimer,我得到了以下日志:

Fired: 2018-03-18 13:22:17 +0000
Timer fired: 2018-03-18 13:22:17 +0000
Fired: 2018-03-18 13:22:18 +0000
Timer fired: 2018-03-18 13:22:18 +0000
Fired: 2018-03-18 13:22:19 +0000
Fired: 2018-03-18 13:22:20 +0000
Timer fired: 2018-03-18 13:22:20 +0000
Fired: 2018-03-18 13:22:21 +0000
Fired: 2018-03-18 13:22:22 +0000
Fired: 2018-03-18 13:22:23 +0000
Fired: 2018-03-18 13:22:24 +0000
Fired: 2018-03-18 13:22:25 +0000
Fired: 2018-03-18 13:22:26 +0000
Fired: 2018-03-18 13:22:27 +0000
Fired: 2018-03-18 13:22:28 +0000
Fired: 2018-03-18 13:22:29 +0000
Fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Fired: 2018-03-18 13:22:31 +0000
Timer fired: 2018-03-18 13:22:31 +0000
Fired: 2018-03-18 13:22:32 +0000
Timer fired: 2018-03-18 13:22:32 +0000

说“已触发”的是DispatchSourceTimer 的实际偶数处理程序,而“定时器已触发”位于更新标签等的处理程序函数中。很明显,您可以看到计时器工作正常,但似乎主线程被大量使用。

这是我的代码:

//timer is a DispatchSourceTimer, queue is a concurrent custom queue
public func startTimer() {
    timer?.cancel()
    timer = DispatchSource.makeTimerSource(queue: queue)
    timer?.schedule(deadline: DispatchTime.now(), repeating: .seconds(1))
    timer?.setEventHandler { [weak self] in
        print("Fired: \(Date())")
        DispatchQueue.main.async {
            self?.notifyObservers()
        }
    }
    timer?.resume()
}

public func stopTimer() {
    timer?.cancel()
    timer = nil
}

这里是通知观察者:

private func notifyObservers() {
    for observer: TimeObserver in self.timeObservers {
        observer.timerFired()
    }
}

我维护了一个对象的观察者数组,这些对象希望在定时器触发时得到通知。

我做了一个测试,删除了 for 循环,只通知了列表中的第一个观察者,它确实加快了一些速度,但仍然没有解决问题。

如果我只是播放歌曲并且几秒钟后主线程实际上可以处理它并且我可以完美地显示时间,但是在执行繁重的事情时,例如使用 MPMediaPlayer 框架更改歌曲或调用 @987654329 @ 和 pause() 上的 MPMusicPlayerController 它开始滞后。

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 您是否曾经在任何地方使您的计时器失效? timer?.invalidate()
  • 不,我为 Timer 对象做了。不适用于 DispatchSourceTimer,它没有 invalidate() 函数

标签: ios swift grand-central-dispatch nstimer mpmediaplayercontroller


【解决方案1】:

如果你像这样实例化你的播放器,你可以看到这种行为:

let player = MPMusicPlayerController()

但是,如果您使用:

let player = MPMusicPlayerController.applicationQueuePlayer

或:

let player = MPMusicPlayerController.applicationMusicPlayer

在我的 iPhone X 上,前一种技术会导致 5-8 秒的主线程阻塞,而后两种技术只会在 1 秒或更短的时间内阻塞主线程。


我假设您的调度计时器是为了测试正在发生的事情,但是如果它要做的只是将它调度回主队列,那么在后台线程上使用这个调度计时器是没有意义的。如果您的主线程由于您无法控制的原因而被阻塞,您不希望在主线程中积压大量无法及时处理的更新。

我建议恢复为简单的Timer

【讨论】:

  • 是的,我会回到一个简单的计时器,因为这不是问题。问题是 MPMusicPlayerController。问题是,我的应用程序的内容依赖于系统音乐播放器,所以 applicationMusicPlayer 并不是一个真正的选择
  • 我确实看到了您用let player = MPMusicPlayerController.systemMusicPlayer 描述的行为。如果您使用的是系统音乐播放器,您可能不得不忍受 UI 中的这种暂时中断。但希望其他人可能已经找到了解决这个问题的方法。我希望 prepareToPlay 能消除这种延迟,但它没有。
  • 我想知道 Apple 在内部是如何做到这一点的,以及为什么它不能在公共 API 中解决这个问题......因为我怀疑它与我在主线程上做的其他事情有关。
  • 不,我确定这不是你在主线程上所做的。我创建了一个原始播放器,在主线程上什么都不做(除了更新标签中的currentPlaybackTime),我看到了你描述的相同行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多