ios音乐后台播放和远程控制

最近在做音乐播放器,要静音状态下播放,后台播放,远程控制,锁屏显示,上拉菜单控制,来电中断处理等。

开启后台模式:
在TARGETS-Capabilities-Background Modes 勾选Audio,AirPay and Picture in Picture

在AppDelegate中

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //静音状态下播放
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    //处理电话打进时中断音乐播放
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interruptionNotificationHandler:) name:AVAudioSessionInterruptionNotification object:nil];
    //后台播放
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

    // 在App启动后开启远程控制事件, 接收来自锁屏界面和上拉菜单的控制
    [application beginReceivingRemoteControlEvents];
    // 处理远程控制事件
    [self remoteControlEventHandler];
    
    self.window= [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.window makeKeyAndVisible];
    
    MusicListTableVC * vc = [[MusicListTableViewController alloc] init];
    UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:vc];
    self.window.rootViewController = nc;
    
    return YES;
}

来电中断处理:

//来电中断处理
- (void)interruptionNotificationHandler:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;
    NSString *type = [NSString stringWithFormat:@"%@", [interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey]];
    NSUInteger interuptionType = [type integerValue];

    if (interuptionType == AVAudioSessionInterruptionTypeBegan) {
        //获取中断前音乐是否在播放
        _played = [MusicPlayViewController shareMusicPlay].isPlaying;
        NSLog(@"AVAudioSessionInterruptionTypeBegan");
    }else if (interuptionType == AVAudioSessionInterruptionTypeEnded) {
        NSLog(@"AVAudioSessionInterruptionTypeEnded");
    }
    
    if(_played)
    {
        //停止播放的事件
        [[MusicPlayTools shareMusicPlay] musicPause];
        _played=NO;
    }else {
        //继续播放的事件
        [[MusicPlayTools shareMusicPlay] musicPlay];
        _played=YES;
    }
}
- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"要挂起了。。。");
    //更新锁屏信息
    [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // 在App要终止前结束接收远程控制事件, 也可以在需要终止时调用该方法终止
    [application endReceivingRemoteControlEvents];
}

远程控制事件处理:

// 在需要处理远程控制事件的具体控制器或其它类中实现
- (void)remoteControlEventHandler
{
    // 直接使用sharedCommandCenter来获取MPRemoteCommandCenter的shared实例
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    // 启用播放命令 (锁屏界面和上拉快捷功能菜单处的播放按钮触发的命令)
    commandCenter.playCommand.enabled = YES;
    // 为播放命令添加响应事件, 在点击后触发
    [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        [[MusicPlayTools shareMusicPlay] musicPlay];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    // 播放, 暂停, 上下曲的命令默认都是启用状态, 即enabled默认为YES
    [commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        //点击了暂停
        [[MusicPlayTools shareMusicPlay] musicPause];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    [commandCenter.previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        //点击了上一首
        [[MusicPlayViewController shareMusicPlay] lastSongAction];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    [commandCenter.nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        //点击了下一首
        [[MusicPlayViewController shareMusicPlay] nextSongButtonAction:nil];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    // 启用耳机的播放/暂停命令 (耳机上的播放按钮触发的命令)
    commandCenter.togglePlayPauseCommand.enabled = YES;
    // 为耳机的按钮操作添加相关的响应事件
    [commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        // 进行播放/暂停的相关操作 (耳机的播放/暂停按钮)
        [[MusicPlayViewController shareMusicPlay] playPauseButtonAction:nil];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
}

锁频界面上所显示的歌曲播放信息和图片,控制中心上显示的歌曲播放信息等,这些信息的显示都由MPNowPlayingInfoCenter类来控制。
首先#import 然后调用MPNowPlayingInfoCenter的单例方法获取实例,再把需要显示的信息组织成Dictionary并赋值给nowPlayingInfo属性就完成了。

其中常用的是MPNowPlayingInfoPropertyElapsedPlaybackTime和MPNowPlayingInfoPropertyPlaybackRate:

  • MPNowPlayingInfoPropertyElapsedPlaybackTime表示已经播放的时间,用这个属性可以让NowPlayingCenter显示播放进度;
  • MPNowPlayingInfoPropertyPlaybackRate表示播放速率。通常情况下播放速率为1.0,即真是时间的1秒对应播放时间中的1秒;

这里需要解释的是,NowPlayingCenter中的进度刷新并不是由app不停的更新nowPlayingInfo来做的,而是根据app传入的ElapsedPlaybackTime和PlaybackRate进行自动刷新。例如传入ElapsedPlaybackTime=120s,PlaybackRate=1.0,那么NowPlayingCenter会显示2:00并且在接下来的时间中每一秒把进度加1秒并刷新显示。如果需要暂停进度,传入PlaybackRate=0.0即可。

所以每次播放暂停和继续都需要更新NowPlayingCenter并正确设置ElapsedPlaybackTime和PlaybackRate否则NowPlayingCenter中的播放进度无法正常显示。

NowPlayingCenter的刷新时机

频繁的刷新NowPlayingCenter并不可取,特别是在有Artwork的情况下。所以需要在合适的时候进行刷新。

依照我自己的经验下面几个情况下刷新NowPlayingCenter比较合适:

  • 当前播放歌曲进度被拖动时
  • 当前播放的歌曲变化时
  • 播放暂停或者恢复时
  • 当前播放歌曲的信息发生变化时(例如Artwork,duration等)

在刷新时可以适当的通过判断app是否active来决定是否必须刷新以减少刷新次数。

/**
 *  设置锁屏信息
 */
-(void)configNowPlayingInfoCenter
{
    Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
    
    if (playingInfoCenter) {
        MusicInfoModel * model = [MusicPlayTools shareMusicPlay].model;
        NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld", self.index + 1]];
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithBoundsSize:image.size requestHandler:^UIImage * _Nonnull(CGSize size) {
            return image;
        }];
        //歌曲名称
        [songInfo setObject:model.name forKey:MPMediaItemPropertyTitle];
        //演唱者
        [songInfo setObject:model.singer forKey:MPMediaItemPropertyArtist];
        //专辑名
        [songInfo setObject:@"专辑名" forKey:MPMediaItemPropertyAlbumTitle];
        //专辑缩略图
        [songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
        //音乐当前已经播放时间
        NSInteger currentTime = [[MusicPlayTools shareMusicPlay] getCurTime];
        [songInfo setObject:[NSNumber numberWithInteger:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
        //进度光标的速度 (这个随 自己的播放速率调整,我默认是原速播放)
        [songInfo setObject:[NSNumber numberWithFloat:1.0] forKey:MPNowPlayingInfoPropertyPlaybackRate];
        //歌曲总时间设置
        NSInteger duration = [model.duration integerValue];
        [songInfo setObject:[NSNumber numberWithInteger:duration] forKey:MPMediaItemPropertyPlaybackDuration];
        //设置锁屏状态下屏幕显示音乐信息
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
    }
}

你可能感兴趣的:(ios音乐后台播放和远程控制)