美文网首页iOS 技巧
【iOS】后台播报TTS(防止APP后台被杀死)

【iOS】后台播报TTS(防止APP后台被杀死)

作者: 在这蓝色天空下 | 来源:发表于2021-05-19 18:57 被阅读0次

    需求分析

    1、APP进入后台,防止被杀死,保持socket链接;
    2、socket接收到消息,通过TTS播报处理;

    实现计划

    1、实现类似QQ一样的,APP进入后台,可以在锁屏页面继续播放音乐,而这个时候APP是不会被杀死的;
    2、APP播放一个静音文件,循环播放;
    3、当接收到socket消息时,暂停音乐播放,TTS播报socket消息;
    4、TTS播报完毕,继续播放静音文件;
    5、禁止掉锁屏页面按钮功能(上一首、下一首、开始、暂停);

    具体实现

    • 1.项目设置

    在这里插入图片描述
    • 2.具体实现

    #import "ViewController.h"
    #import <AVFoundation/AVFoundation.h>
    #import <MediaPlayer/MediaPlayer.h>
    
    @interface ViewController ()<AVSpeechSynthesizerDelegate>
    
    @property (nonatomic, strong)AVPlayer *player;
    @property (nonatomic, strong)AVSpeechSynthesizer *synthesizer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        // 开始播放音乐
        [self starPlayMusic];
        // 今天播放进度
        [self playerPressHandle];
        // 关闭锁屏页面按钮交互
        [self createRemoteCommandCenter];
        
        // 模拟socket 接收消息
        [self socketReceiveMessage];
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
        
        MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
        [[commandCenter playCommand] removeTarget:self];
        [[commandCenter pauseCommand] removeTarget:self];
        [[commandCenter nextTrackCommand] removeTarget:self];
        [[commandCenter previousTrackCommand] removeTarget:self];
        [commandCenter.changePlaybackPositionCommand removeTarget:self];
        
    }
    
    #pragma mark - 模拟socket接收消息
    - (void)socketReceiveMessage
    {
        int number = random() % 180+60;
        NSLog(@"%d 秒后 tts播报", number);
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (ino64_t)(number * NSEC_PER_SEC));
        
        __weak __typeof__(self) weakSelf = self;
        dispatch_after(time, dispatch_get_main_queue(), ^{
            [weakSelf pauseMusic];
            [weakSelf speekWithString: [NSString stringWithFormat:@"新客户来了 %ld", random() % 100]];
            
            NSLog(@"%f", [[NSDate date] timeIntervalSinceNow]);
            [weakSelf socketReceiveMessage];
        });
    }
        
    
    #pragma mark - 直接播放音乐
    - (void)starPlayMusic
    {
        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:@"http://atyk.jzhunk.xyz/v130/upload/1.mp3"]];
        [self.player replaceCurrentItemWithPlayerItem:playerItem];
        [self playMusic];
    }
    
    
    #pragma mark - 播放进度
    - (void)playerPressHandle
    {
        __weak __typeof__(self) weakSelf = self;
        [self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, NSEC_PER_SEC) queue:NULL usingBlock:^(CMTime time) {
            //进度 当前时间/总时间
            CGFloat progress = CMTimeGetSeconds(weakSelf.player.currentItem.currentTime) / CMTimeGetSeconds(weakSelf.player.currentItem.duration);
            NSLog(@"进度 == %f", progress);
            if (progress == 1.0f || progress >= 0.99f) {
                NSLog(@"播放完毕");
                //播放百分比为1表示已经播放完毕
                //循环播放,防止APP被杀死
                [weakSelf starPlayMusic];
            }
        }];
    }
    
    
    
    #pragma mark - 初始化音乐播放器
    - (AVPlayer *)player
    {
        if (!_player) {
            _player = [[AVPlayer alloc] init];
        }
        return _player;
    }
    
    #pragma mark - 初始化tts播放器
    -(AVSpeechSynthesizer *)synthesizer
    {
        if (!_synthesizer) {
            _synthesizer = [[AVSpeechSynthesizer alloc]init];
            _synthesizer.delegate = self;
        }
        return _synthesizer;
    }
    
    #pragma mark - tts播报
    - (void)speekWithString:(NSString *)value
    {
        AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:value];
        utterance.pitchMultiplier=0.8;
        //中式发音
        AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];
        utterance.voice = voice;
        [self.synthesizer speakUtterance:utterance];
    }
    
    #pragma mark - 处理锁屏页交互
    - (void)createRemoteCommandCenter
    {
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
        
        // 暂停
        MPRemoteCommand *pauseCommand = [commandCenter pauseCommand];
        [pauseCommand setEnabled:NO];
        [pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        // 开始
        MPRemoteCommand *playCommand = [commandCenter playCommand];
        [playCommand setEnabled:NO];
        [playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            NSLog(@"开始");
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        // 下一首
        MPRemoteCommand *nextCommand = [commandCenter nextTrackCommand];
        [nextCommand setEnabled:NO];
        [nextCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            NSLog(@"下一首");
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        // 上一首
        MPRemoteCommand *previousCommand = [commandCenter previousTrackCommand];
        [previousCommand setEnabled:NO];
        [previousCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            NSLog(@"上一首");
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        
        // 进度条
        MPRemoteCommand *changePlaybackPositionCommand = [commandCenter changePlaybackPositionCommand];
        [changePlaybackPositionCommand setEnabled:NO];
        [changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            NSLog(@"进度条");
            return MPRemoteCommandHandlerStatusSuccess;
        }];
    }
    
    #pragma mark - 更新锁屏界面信息
    - (void)updateLockScreenInfo {
        
        // 1.获取锁屏中心
        MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
        // 初始化一个存放音乐信息的字典
        NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];
        
        // 2、设置歌曲名
        [playingInfoDict setObject:[NSString stringWithFormat:@"歌曲1111"]
                            forKey:MPMediaItemPropertyTitle];
        [playingInfoDict setObject:[NSString stringWithFormat:@"专辑2222"]
                            forKey:MPMediaItemPropertyAlbumTitle];
        
        // 3、设置封面的图片
        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"image.jpg"]];
        if (image) {
            MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
            [playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork];
        }
        
        // 4、设置播放速度
        [playingInfoDict setObject:@(_player.rate) forKey:MPNowPlayingInfoPropertyPlaybackRate];
        
        //音乐信息赋值给获取锁屏中心的nowPlayingInfo属性
        playingInfoCenter.nowPlayingInfo = playingInfoDict;
    }
    
    #pragma mark - 开始播放音乐
    - (void)playMusic
    {
        if (!self.player) {
            return;
        }
        [self.player play];
        [self updateLockScreenInfo];
    }
    
    #pragma mark - 暂停播放音乐
    - (void)pauseMusic
    {
        if (!self.player) {
            return;
        }
        
        [self.player pause];
        
        [self updateLockScreenInfo];
        
    }
    
    #pragma mark - AVSpeechSynthesizerDelegate
    - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didStartSpeechUtterance:(AVSpeechUtterance *)utterance
    {
        NSLog(@"开始tts播报");
    }
    
    - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
    {
        NSLog(@"结束tts播报");
        [self starPlayMusic];
    }
    
    
    @end
    
    

    遇到的问题

    1、APP进入后台,音乐播放被暂停,长时间APP会被杀死

    处理:通过监听APP进入前/后台,让音乐可以继续播放

    #pragma mark - 监听APP进入前/后台(处理APP进入后台,音乐播放暂停问题)
    - (void)registerAllNotifications
    {
        // 后台通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(apllicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
     
        // 进入前台通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(apllicationWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
    }
    
    #pragma mark - 移除监听
    - (void)removeAllNotifications {
     
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
     
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
        
    }
    
    #pragma mark - 进入后台通知
    - (void)apllicationWillResignActiveNotification:(NSNotification *)n
    {
        NSError *error = nil;
        // 后台播放代码
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setActive:YES error:&error];
        if(error) {
            NSLog(@"ListenPlayView background error0: %@", error.description);
        }
        //后台播放
        [session setCategory:AVAudioSessionCategoryPlayback error:&error];
        if(error) {
            NSLog(@"ListenPlayView background error1: %@", error.description);
        }
        //开启后台处理多媒体事件
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    }
    
    #pragma mark - 进入前台通知
    - (void)apllicationWillEnterForegroundNotification:(NSNotification *)n {
        // 进前台 设置不接受锁屏页面控制
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
    
    

    最终结果

    在这里插入图片描述
    demo地址:https://download.csdn.net/download/tianzhilan0/15045404
    demo地址:https://github.com/tianzhilan0/TTSBackPlayer

    相关文章

      网友评论

        本文标题:【iOS】后台播报TTS(防止APP后台被杀死)

        本文链接:https://www.haomeiwen.com/subject/rmnqjltx.html