问题
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:
- Pause the audio play in app.
- Lock the screen via power button, then reopen the screen and click play button on control panel.
- 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