提问者:小点点

iOS:当Spotify用作遥控器时,它是如何在后台保持活动的?


据我所知,你的应用程序在后台运行时会被杀死,除非你真的通过音频会话播放一些可听到的声音,但Spotify作为一个远程控制应用程序,尽管它不可见,不播放音频,不跟踪位置等,但它还是可以在某种程度上保持活力。它在后台运行时,可以对音量变化和服务器通信做出反应。 怎么做?

Spotify实现了他们自己的基于网络的跨平台远程控制应用程序。

出于某种原因,我找不到很多关于背景模式的信息来说明他们可能在做什么。 我找不到包含远程控制的文档用例。

对于那些怀疑Spotify是否提供了自己的远程控制解决方案的人,我禁用了蓝牙和WiFi,并继续使用它,就好像什么都没发生过一样。 这不是关于AirPlay,蓝牙等等。

iOS 13


共1个答案

匿名用户

您需要使用UIApplication.Shared.BeginReceivingRemoteControlevents()

当您订阅了它,同时也进行了订阅(示例):

MPRemoteCommandCenter.shared().pauseCommand.addTarget { [weak self] _ in
        self?.player.pause()
        return .success
    }

    MPRemoteCommandCenter.shared().playCommand.addTarget { [weak self] _ in
        self?.player.play()
        return .success
    }

    MPRemoteCommandCenter.shared().stopCommand.addTarget { [weak self] _ in
        self?.player.stop()
        return .success
    }

    MPRemoteCommandCenter.shared().previousTrackCommand.addTarget { [weak self] _ in
        self?.player.previousTrack()
        return .success
    }

    MPRemoteCommandCenter.shared().nextTrackCommand.addTarget { [weak self] _ in
        self?.player.nextTrack()
        return .success
    }

    MPRemoteCommandCenter.shared().skipBackwardCommand.addTarget { [weak self] event in
        guard let self = self, let event = event as? MPSkipIntervalCommandEvent else { return .commandFailed }
        self.player.skipBackward()
        self.updateNowPlayingInfo()
        return .success
    }

    MPRemoteCommandCenter.shared().skipForwardCommand.addTarget { [weak self] event in
        guard let self = self, let event = event as? MPSkipIntervalCommandEvent else { return .commandFailed }
        self.player.skipForward()
        self.updateNowPlayingInfo()
        return .success
    }

    MPRemoteCommandCenter.shared().changePlaybackPositionCommand.addTarget { [weak self] event in
        if let event = event as? MPChangePlaybackPositionCommandEvent {
            self?.player.seek(to: event.positionTime)
        }
        return .success
    }

现在设置您的音频会话:

do {
        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, policy: .longFormAudio)
        try AVAudioSession.sharedInstance().setActive(true, options: [])
    } catch {
        log.error(error)
    }

以上内容在以下位置进行了说明:https://developer.apple.com/documentation/avfoundation/media_assets_playback_and_editing/creating_a_basic_video_player_ios_and_tvos/controlling_background_audio

您现在需要一些通知,以了解应用程序何时进入后台。 如果您正在播放视频,没有这个功能它将无法工作(如果您正在播放音频,没有这个功能它就可以正常工作。。无论如何,这样做是一个好主意,因为您永远不知道需求何时发生变化):

notificationObservers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { [weak self] _ in
    guard let self = self else { return }
    (self.layer as? AVPlayerLayer)?.player = nil
})


notificationObservers.append(NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main) { [weak self] _ in
    guard let self = self else { return }
    (self.layer as? AVPlayerLayer)?.player = self.player
})

以上内容可在以下网站上查阅:https://developer.apple.com/documentation/avfoundation/media_assets_playback_and_editing/creating_a_basic_video_player_ios_and_tvos/playing_audio_from_a_video_asset_in_the_backgrounding

当用户按下media centre上的任何一个按钮时,应用程序将在回调中接收事件。 你可以像这样回应它:

MPNowPlayingInfoCenter.default().nowPlayingInfo = [
    MPNowPlayingInfoPropertyMediaType: "Audio",
    MPMediaItemPropertyTitle: nowPlayingItem.name,
    MPMediaItemPropertyArtist: nowPlayingItem.artist,
    MPMediaItemPropertyPlaybackDuration: TimeInterval(nowPlayingItem.duration),
    MPNowPlayingInfoPropertyPlaybackRate: 1.0,
    MPNowPlayingInfoPropertyPlaybackProgress: Float(0.0),
    MPNowPlayingInfoPropertyElapsedPlaybackTime: Double(player.currentTime().seconds)
]

它将更新媒体中心,您的应用程序将相应地响应播放所请求的歌曲,并负责更新显示的信息和控件。

别忘了设置背景模式:

这款应用可以在锁屏和后台播放任何来源的视频。 我测试了Spotify,这是完全相同的行为,我正在我的应用程序和完全相同的行为描述在这篇文章。