AVPlayer audioSessionGotInterrupted通知从后台唤醒



我使用AVAudioPlayer来播放音频。我已经启用了背景音频,并且音频会话配置正确。

我实现了audioSessionGotInterrupted方法,以便在音频会话中断时得到通知。这是我当前的代码:

@objc private func audioSessionGotInterrupted(note: NSNotification) {
    guard let userInfo = note.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
            return
    }
    if type == .began {
        print("interrupted")
        // Interruption began, take appropriate actions
        player.pause()
        saveCurrentPlayerPosition()
    }
    else if type == .ended {
        if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
            let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
            if options == .shouldResume {
                print("restored")
                // Interruption Ended - playback should resume
                setupPlayer()
                player.play()
            } else {
                // Interruption Ended - playback should NOT resume
                // just keep the player paused
            }
        }
    }
}

现在我做以下操作:

  • 播放一些音频
  • 锁定手机
  • 暂停音频
  • 等待几秒钟,直到我在XCode调试器中看到应用程序已在后台停止
  • 我在锁屏中点击播放

我的commandCenter play()方法按预期调用。然而audioSessionGotInterrupted方法也被type == .began调用。

这怎么可能?我希望不会看到这样的通知,或者至少.ended

我用的是iOS 10 beta 8

检查

https://developer.apple.com/documentation/avfoundation/avaudiosession/1616596-interruptionnotification

从iOS 10开始,系统将关闭大多数应用程序的音频会话,以响应应用程序进程被暂停。当应用程序再次开始运行时,它将收到一个中断通知,表明其音频会话已被系统停用。此通知在时间上必然延迟,因为它只能在应用程序再次运行时发送。如果你的应用程序的音频会话因为这个原因被挂起,userInfo字典将包含AVAudioSessionInterruptionWasSuspendedKey键值为true。

如果您的音频会话被配置为非混音(播放,playAndRecord, soloAmbient和multiRoute类别的默认行为),建议您停用您的音频会话,如果您在进入后台时没有积极使用音频。这样做可以避免你的音频会话被系统停用(并收到这个有点令人困惑的通知)。

if let reason = AVAudioSessionInterruptionType(rawValue: reasonType as! UInt) {
  switch reason {
  case .began:
    var shouldPause = true
    if #available(iOS 10.3, *) {
      if let _ = notification.userInfo?[AVAudioSessionInterruptionWasSuspendedKey] {
        shouldPause = false
      }
    }
    if shouldPause {
      self.pause()
    }
    break
  case .ended:
    break
  }
}

虽然上面的答案没有错,但它仍然在我的应用程序中造成了很多麻烦,并为检查多种情况编写了很多样板代码。

如果你阅读AVAudioSessionInterruptionWasSuspendedKey的描述,它说如果你没有在你的应用程序被发送到后台之前停用你的音频会话(每次你锁定屏幕时都会发生),则会抛出通知

要解决这个问题,你只需要在应用程序发送到后台时没有声音播放时停用会话,然后在声音播放时重新激活会话。之后,您将不会收到AVAudioSessionInterruptionWasSuspendedKey通知。

   NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: .main) { sender in
        guard self.player.isPlaying == false else { return }
        self.setSession(active: false)
    }
    NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main) { sender in
        guard self.player.isPlaying else { return }
        self.setSession(active: true)
    }
func setSession(active: Bool) -> Bool {
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playback, mode: .default)
        try session.setActive(active)
        return true
    } catch let error {
        print("*** Failed to activate audio session: (error.localizedDescription)")
        return false
    }
}

注意:激活会话可能不是必需的,因为它是由Apple的内部播放类处理的(例如AVPlayer),但是手动执行是一个很好的实践。

最新更新