MPRemoteCommandCenter pause/play button create more sharedInstance of AVAudioPlayer?

こ雲淡風輕ζ 提交于 2020-01-25 06:50:29

问题


My app is a music player using AVAudioPlayer with Singleton pattern and background mode. Things are going well, but after I add command center for locked screen control, one strange behavior I found.

When I use the pause/play button in locked screen, sometimes it may create additional sharedInstance of audioPlay on background, because I found more than ones times duplicate printing, e.g. in my commandCenter.pauseCommand code. And audio is paused or resume playing but next click on both command center button and play/pause button in app's VC have no effect. Is that a multi-thread issue, well I'm very new in Swift, any help or hint about how to locate/solve this issue is appreciate.

Update, one scenario to repeat:

  1. Pause the audio play in app.
  2. Lock the screen via power button, then reopen the screen and click play button on control panel.
  3. Audio resume to play on locked screen and elapsed time continue to process then open app again, process bar is still stopped at previous position, and next time play/pause clicking has no effect.

/ / / the code of command center and MPMediaItemProperty

func setUpLockedScreenControl() {
        // add control panel on locked screen
        let commandCenter = MPRemoteCommandCenter.shared()
        commandCenter.pauseCommand.isEnabled = true
        commandCenter.playCommand.isEnabled = true
        commandCenter.nextTrackCommand.isEnabled = true
        commandCenter.previousTrackCommand.isEnabled = true

        commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            SongData.isPlaying = false
            AudioManager.sharedInstance.pauseMusic()

            self.updateLockedScreenPlayingInfo()
            print("pause music in command center")
            self.playOrPauseButton.setImage(UIImage(named: "Play"), for: .normal)
            self.playOrPauseButton.imageView?.contentMode = .scaleAspectFit

            return .success
        }

        commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            SongData.isPlaying = true
            AudioManager.sharedInstance.playMusic()

            self.updateLockedScreenPlayingInfo()
            print("play music in command center")
            // add to continue update playing progress
            self.playOrPauseButton.setImage(UIImage(named: "Pause"), for: .normal)
            self.playOrPauseButton.imageView?.contentMode = .scaleAspectFit
            return .success
        }

        commandCenter.nextTrackCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            self.goNextSong()
            self.updateLockedScreenPlayingInfo()
            return .success
        }

        commandCenter.previousTrackCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            self.goPreviousSong()
            self.updateLockedScreenPlayingInfo()
            return .success
        }
    }

    func updateLockedScreenPlayingInfo() {

        var info  = Dictionary<String, Any>()
        info[MPMediaItemPropertyTitle] = SongData.songList[SongData.currentTrack].songName
        info[MPMediaItemPropertyAlbumTitle] = SongData.songList[SongData.currentTrack].albumName
        info[MPMediaItemPropertyArtist] = SongData.songList[SongData.currentTrack].artistName

        let image = SongData.songList[SongData.currentTrack].albumArtwork
        let artwork = MPMediaItemArtwork.init(boundsSize: image.size, requestHandler: { (size) -> UIImage in
                return image
        })

        info[MPMediaItemPropertyArtwork] = artwork
        info[MPMediaItemPropertyPlaybackDuration] = AudioManager.sharedInstance.audioPlayer.duration

        if SongData.isPlaying == true {
            let time = AudioManager.sharedInstance.audioPlayer.currentTime
            info[MPNowPlayingInfoPropertyElapsedPlaybackTime] = time
            info[MPMediaItemPropertyPlaybackDuration] = AudioManager.sharedInstance.audioPlayer.duration
            info[MPNowPlayingInfoPropertyPlaybackRate] = 1.0
            print("SongData.isPlaying == true, current time is:", time)
        } else {
            let time = AudioManager.sharedInstance.audioPlayer.currentTime
            info[MPNowPlayingInfoPropertyElapsedPlaybackTime] = time
            info[MPMediaItemPropertyPlaybackDuration] = AudioManager.sharedInstance.audioPlayer.duration
            info[MPNowPlayingInfoPropertyPlaybackRate] = 0.0
            print("SongData.isPlaying == false, current time is:", time)
        }

        MPNowPlayingInfoCenter.default().nowPlayingInfo = info
    }

/ / / here is the console info when bug happened, in this case, twice printing of commandCenter.playCommand code

Playback OK
Session is Active
2019-10-23 11:38:06.331882+0800 LocalMusicPlayer[12366:853141] Can't end BackgroundTask: no background task exists with identifier 22 (0x16), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
SongData.isPlaying == true, current time is: 5.15344671201814
play music in command center
SongData.isPlaying == true, current time is: 5.155759637188209
play music in command center

/ / / background mode set in appDelegate, and the Can't end BackgroundTask only occur on iOS 13, I think it could be an issue on Apple side.

func applicationWillResignActive(_ application: UIApplication) {

    do {
        // set options as empty [] could show control panel in locked screen.
        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
        print("Playback OK")
        try AVAudioSession.sharedInstance().setActive(true)
        print("Session is Active")
     } catch {
        print(error)
     }
}

/ / / Audio Manager.swift for audioPlayer control

class AudioManager: NSObject, AVAudioPlayerDelegate {
    // use Singleton pattern keep the music continuing when user move through the App.
    static let sharedInstance = AudioManager()

    var audioPlayer: AVAudioPlayer!
    var playingSong: SongData?

    private override init() {
        super.init()  
    }
... ... 
}

/ / / Play/Pause button in app's VC

@IBAction func PlayOrPauseMusic(_ sender: UIButton) {
        if SongData.isPlaying {
            print("pause the music playing")
            SongData.isPlaying = false
            AudioManager.sharedInstance.pauseMusic()

            updateLockedScreenPlayingInfo()
            playOrPauseButton.setImage(UIImage(named: "Play"), for: .normal)
            playOrPauseButton.imageView?.contentMode = .scaleAspectFit
        } else {
            print("continue to play current music")
            SongData.isPlaying = true
            AudioManager.sharedInstance.playMusic()

            updateLockedScreenPlayingInfo()
            playOrPauseButton.setImage(UIImage(named: "Pause"), for: .normal)
            playOrPauseButton.imageView?.contentMode = .scaleAspectFit
        }
    }

来源:https://stackoverflow.com/questions/58515281/mpremotecommandcenter-pause-play-button-create-more-sharedinstance-of-avaudiopla

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!