iOS:后台播放及锁屏控制

一.添加后台播放权限

  1. 修改Info.plist
    在 Info.plist 中添加 Required background modes,并在下面添加一项 :App plays audio or streams audio/video using AirPlay .
  2. 修改 Capabilities
    Capabilities 中开启 Background Modes 。如图所示:
    iOS:后台播放及锁屏控制_第1张图片
    Enable Background Modes
  3. 修改 AppDelegate
    AppDelegateapplication: didFinishLaunchingWithOptions: 方法中添加以下代码:
// 告诉app支持后台播放
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];

二.添加锁屏播放控制

1. 用 MPNowPlayingInfoCenter 显示歌曲信息

播放信息可以通过[MPNowPlayingInfoCenter defaultCenter]. nowPlayingInfo进行设置,如下

- (void)updateNowPlayingInfoWithItem:(nullable WXPlayingItem *)item{
    if (item == nil) {
        [self updateNowPlayingInfo:nil];
        return;
    }
    
    NSMutableDictionary *playInfo = [NSMutableDictionary new];
    NSDictionary *curDict = [[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo];
    if (curDict) {
        playInfo = [curDict mutableCopy];
    }
    NSString * songName = item.songName.length > 0? item.songName:KString(@"未知歌曲");
    NSString * singerName = item.singerName.length > 0? item.singerName:KString(@"未知歌手");
    [playInfo setObject:songName forKey:MPMediaItemPropertyTitle];      //歌曲
    [playInfo setObject:singerName forKey:MPMediaItemPropertyArtist];   //歌手
    if (item.albumTitle != nil) {   //专辑
        [playInfo setObject:item.albumTitle forKey:MPMediaItemPropertyAlbumTitle];
    }
    [playInfo setObject:[NSNumber numberWithDouble:item.curTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; //已播放时长
    [playInfo setObject:[NSNumber numberWithDouble:item.totalTime] forKey:MPMediaItemPropertyPlaybackDuration];      //歌曲时长
    
    KLog(@"[WXPlayer] UpdateInfo:songName = %@,singerName = %@,albumTitle = %@,playbackTime = %d,playbackDuration = %d",songName,singerName,item.albumTitle,(int)item.curTime,(int)(item.totalTime - item.curTime));
    
    //设置封面
    if (item.albumImg != nil) {
        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:item.albumImg];
        if (artwork) {
            [playInfo setObject:artwork forKey:MPMediaItemPropertyArtwork];
        }
    }
    [self updateNowPlayingInfo:playInfo];
}

- (void)updateNowPlayingInfo:(NSDictionary *)playInfo{
    KLog(@"[WXPlayer] infoDict=%@", playInfo);
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:playInfo];
    [self startHandler];
    MPRemoteCommandCenter * commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    commandCenter.previousTrackCommand.enabled = self.curPlayer.onRemoteCommand_hasPre;
    commandCenter.nextTrackCommand.enabled = self.curPlayer.onRemoteCommand_hasNext;
}

显示效果如下


锁屏播放效果.png

2. 用 MPRemoteCommandCenter实现播放控制

在 iOS 7.1 之后,可以通过 MPRemoteCommandCenter来控制音频播放。每个控制操作都封装为一个 MPRemoteCommand 对象,给 MPRemoteCommand 添加响应事件有两种方式:

  1. addTargetWithHandler:以 Block 的方式传入响应事件,需要返回 MPRemoteCommandHandlerStatusSuccess 来告知响应成功。
  2. addTarget: action: 因为 MPRemoteCommandCenter 是个单例,所以在 target 的 dealloc 中要记得调用 removeTarget: 。如下所示:
- (void)removeCommandCenterTargets {
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    [commandCenter.playCommand removeTarget:self];
    [commandCenter.pauseCommand removeTarget:self];
    [commandCenter.togglePlayPauseCommand removeTarget:self];
    [commandCenter.nextTrackCommand removeTarget:self];
    [commandCenter.previousTrackCommand removeTarget:self];
    
    if (@available(iOS 9.1, *)) {
        [commandCenter.changePlaybackPositionCommand removeTarget:self];
    }
}

注意:因为 changePlaybackPositionCommand 在 iOS 9.1 以后才可用,所以这里加了系统判断。

以下是监听MPRemoteCommandCenter事件的逻辑

-(void) startHandler{
    if (!self.isInitHandler) {
        //返回值根据需要返回,一般情况下返回MPRemoteCommandHandlerStatusSuccess即可
        MPRemoteCommandCenter * commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
        KS_WEAK_SELF(self);
        commandCenter.playCommand.enabled = YES;
        [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] Play");
            [self.curPlayer onRemoteCommand_play];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        commandCenter.pauseCommand.enabled = YES;
        [commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] Pause");
            [self.curPlayer onRemoteCommand_pause];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        commandCenter.togglePlayPauseCommand.enabled = YES;
        [commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] Play Or Pause");
            [self.curPlayer onRemoteCommand_playOrPause];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        [commandCenter.previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] PlayPre");
            [self.curPlayer onRemoteCommand_playPre];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        [commandCenter.nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] PlayNext")
            [self.curPlayer onRemoteCommand_playNext];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        //9.1以上版本支持
        if(IS_OS_91_OR_LATER) {
            commandCenter.changePlaybackPositionCommand.enabled = YES;
            [commandCenter.changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
                CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
                MPChangePlaybackPositionCommandEvent *positionCommandEvent = SAFE_CAST(event, MPChangePlaybackPositionCommandEvent);
                if (positionCommandEvent == nil) {
                    KLog(@"[WXPlayer] Seek Error")
                    return MPRemoteCommandHandlerStatusCommandFailed;
                }
                NSTimeInterval positionTime = positionCommandEvent.positionTime;
                KLog(@"[WXPlayer] Seek to time=%f", positionTime);
                [self.curPlayer onRemoteCommand_seekTo:positionTime];
                return MPRemoteCommandHandlerStatusSuccess;
            }];
        }
        self.isInitHandler = YES;
    }
}

补上其它代码:
WXPlayerRemoteControlMgr.h

#import 
#import "WXPlayingItem.h"

NS_ASSUME_NONNULL_BEGIN

@protocol WXRemoteCommandProtocol 
-(BOOL) onRemoteCommand_hasPre;
-(BOOL) onRemoteCommand_hasNext;

-(void) onRemoteCommand_playPre;
-(void) onRemoteCommand_playNext;
-(void) onRemoteCommand_play;
-(void) onRemoteCommand_pause;
-(void) onRemoteCommand_seekTo:(NSTimeInterval) positionTime;

@optional
-(void) onRemoteCommand_playOrPause;

@end



@interface WXPlayerRemoteControlMgr : NSObject
DECL_SINGLETON(WXPlayerRemoteControlMgr);

@property(nonatomic,strong,nullable) id curPlayer;
/*
更新时机:
 1.播放器状态变成playing
 2.切歌
 3.seek完之后
 4.暂停/开始播放
 3.封面图下载完成
清空时机:需要stop或interrupt的时候
 */
- (void)updateNowPlayingInfoWithItem:(nullable WXPlayingItem *)item;


@end

NS_ASSUME_NONNULL_END

WXPlayerRemoteControlMgr.m

#import "WXPlayerRemoteControlMgr.h"
#import 

@interface WXPlayerRemoteControlMgr ()
@property (nonatomic,strong) NSMutableArray * targetList;
@property(nonatomic,assign) BOOL isInitHandler;
@end

@implementation WXPlayerRemoteControlMgr
IMPL_SINGLETON(WXPlayerRemoteControlMgr);


- (instancetype)init {
    self = [super init];
    if (self) {
        self.targetList = NSMutableArray.new;
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationBecomeActive:)
                                                     name:UIApplicationDidBecomeActiveNotification object:nil];
    }
    return self;
}


-(void) start{
    if (self.curPlayer != nil) {
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    }
}
-(void) stop{
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}


- (void)applicationBecomeActive:(NSNotification*)notification {
    [self start];
}

- (void)dealloc {
    [self stop];
}

- (void)removeCommandCenterTargets {
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    [commandCenter.playCommand removeTarget:self];
    [commandCenter.pauseCommand removeTarget:self];
    [commandCenter.togglePlayPauseCommand removeTarget:self];
    [commandCenter.nextTrackCommand removeTarget:self];
    [commandCenter.previousTrackCommand removeTarget:self];
    
    if (@available(iOS 9.1, *)) {
        [commandCenter.changePlaybackPositionCommand removeTarget:self];
    }
}


-(void) startHandler{
    if (!self.isInitHandler) {
        //返回值根据需要返回,一般情况下返回MPRemoteCommandHandlerStatusSuccess即可
        MPRemoteCommandCenter * commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
        KS_WEAK_SELF(self);
        commandCenter.playCommand.enabled = YES;
        [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] Play");
            [self.curPlayer onRemoteCommand_play];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        commandCenter.pauseCommand.enabled = YES;
        [commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] Pause");
            [self.curPlayer onRemoteCommand_pause];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        commandCenter.togglePlayPauseCommand.enabled = YES;
        [commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] Play Or Pause");
            [self.curPlayer onRemoteCommand_playOrPause];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        [commandCenter.previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] PlayPre");
            [self.curPlayer onRemoteCommand_playPre];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        
        [commandCenter.nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
            KLog(@"[WXPlayer] PlayNext")
            [self.curPlayer onRemoteCommand_playNext];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        //9.1以上版本支持
        if(IS_OS_91_OR_LATER) {
            commandCenter.changePlaybackPositionCommand.enabled = YES;
            [commandCenter.changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
                CHECK_SELF_AND_RETURN_WITH(MPRemoteCommandHandlerStatusCommandFailed);
                MPChangePlaybackPositionCommandEvent *positionCommandEvent = SAFE_CAST(event, MPChangePlaybackPositionCommandEvent);
                if (positionCommandEvent == nil) {
                    KLog(@"[WXPlayer] Seek Error")
                    return MPRemoteCommandHandlerStatusCommandFailed;
                }
                NSTimeInterval positionTime = positionCommandEvent.positionTime;
                KLog(@"[WXPlayer] Seek to time=%f", positionTime);
                [self.curPlayer onRemoteCommand_seekTo:positionTime];
                return MPRemoteCommandHandlerStatusSuccess;
            }];
        }
        self.isInitHandler = YES;
    }
}


- (void)updateNowPlayingInfoWithItem:(nullable WXPlayingItem *)item{
    if (item == nil) {
        [self updateNowPlayingInfo:nil];
        return;
    }
    
    NSMutableDictionary *playInfo = [NSMutableDictionary new];
    NSDictionary *curDict = [[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo];
    if (curDict) {
        playInfo = [curDict mutableCopy];
    }
    NSString * songName = item.songName.length > 0? item.songName:KString(@"未知歌曲");
    NSString * singerName = item.singerName.length > 0? item.singerName:KString(@"未知歌手");
    [playInfo setObject:songName forKey:MPMediaItemPropertyTitle];      //歌曲
    [playInfo setObject:singerName forKey:MPMediaItemPropertyArtist];   //歌手
    if (item.albumTitle != nil) {   //专辑
        [playInfo setObject:item.albumTitle forKey:MPMediaItemPropertyAlbumTitle];
    }
    [playInfo setObject:[NSNumber numberWithDouble:item.curTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; //已播放时长
    [playInfo setObject:[NSNumber numberWithDouble:item.totalTime] forKey:MPMediaItemPropertyPlaybackDuration];      //歌曲时长
    
    KLog(@"[WXPlayer] UpdateInfo:songName = %@,singerName = %@,albumTitle = %@,playbackTime = %d,playbackDuration = %d",songName,singerName,item.albumTitle,(int)item.curTime,(int)(item.totalTime - item.curTime));
    
    //设置封面
    if (item.albumImg != nil) {
        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:item.albumImg];
        if (artwork) {
            [playInfo setObject:artwork forKey:MPMediaItemPropertyArtwork];
        }
    }
    [self updateNowPlayingInfo:playInfo];
}

- (void)updateNowPlayingInfo:(NSDictionary *)playInfo{
    KLog(@"[WXPlayer] infoDict=%@", playInfo);
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:playInfo];
    [self startHandler];
    MPRemoteCommandCenter * commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    commandCenter.previousTrackCommand.enabled = self.curPlayer.onRemoteCommand_hasPre;
    commandCenter.nextTrackCommand.enabled = self.curPlayer.onRemoteCommand_hasNext;
}


@end

你可能感兴趣的:(iOS:后台播放及锁屏控制)