需求分析
1、APP進(jìn)入后臺(tái),防止被殺死,保持socket鏈接;
2、socket接收到消息,通過TTS播報(bào)處理;
實(shí)現(xiàn)計(jì)劃
1、實(shí)現(xiàn)類似QQ一樣的,APP進(jìn)入后臺(tái),可以在鎖屏頁面繼續(xù)播放音樂,而這個(gè)時(shí)候APP是不會(huì)被殺死的;
2、APP播放一個(gè)靜音文件,循環(huán)播放;
3、當(dāng)接收到socket消息時(shí),暫停音樂播放,TTS播報(bào)socket消息;
4、TTS播報(bào)完畢,繼續(xù)播放靜音文件;
5、禁止掉鎖屏頁面按鈕功能(上一首、下一首、開始、暫停);
具體實(shí)現(xiàn)
-
1.項(xiàng)目設(shè)置
在這里插入圖片描述
-
2.具體實(shí)現(xiàn)
#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];
// 今天播放進(jìn)度
[self playerPressHandle];
// 關(guān)閉鎖屏頁面按鈕交互
[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播報(bào)", 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 - 播放進(jìn)度
- (void)playerPressHandle
{
__weak __typeof__(self) weakSelf = self;
[self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, NSEC_PER_SEC) queue:NULL usingBlock:^(CMTime time) {
//進(jìn)度 當(dāng)前時(shí)間/總時(shí)間
CGFloat progress = CMTimeGetSeconds(weakSelf.player.currentItem.currentTime) / CMTimeGetSeconds(weakSelf.player.currentItem.duration);
NSLog(@"進(jìn)度 == %f", progress);
if (progress == 1.0f || progress >= 0.99f) {
NSLog(@"播放完畢");
//播放百分比為1表示已經(jīng)播放完畢
//循環(huán)播放,防止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播報(bào)
- (void)speekWithString:(NSString *)value
{
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:value];
utterance.pitchMultiplier=0.8;
//中式發(fā)音
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;
}];
// 進(jìn)度條
MPRemoteCommand *changePlaybackPositionCommand = [commandCenter changePlaybackPositionCommand];
[changePlaybackPositionCommand setEnabled:NO];
[changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(@"進(jìn)度條");
return MPRemoteCommandHandlerStatusSuccess;
}];
}
#pragma mark - 更新鎖屏界面信息
- (void)updateLockScreenInfo {
// 1.獲取鎖屏中心
MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
// 初始化一個(gè)存放音樂信息的字典
NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];
// 2、設(shè)置歌曲名
[playingInfoDict setObject:[NSString stringWithFormat:@"歌曲1111"]
forKey:MPMediaItemPropertyTitle];
[playingInfoDict setObject:[NSString stringWithFormat:@"專輯2222"]
forKey:MPMediaItemPropertyAlbumTitle];
// 3、設(shè)置封面的圖片
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"image.jpg"]];
if (image) {
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
[playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork];
}
// 4、設(shè)置播放速度
[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播報(bào)");
}
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
NSLog(@"結(jié)束tts播報(bào)");
[self starPlayMusic];
}
@end
遇到的問題
1、APP進(jìn)入后臺(tái),音樂播放被暫停,長時(shí)間APP會(huì)被殺死
處理:通過監(jiān)聽APP進(jìn)入前/后臺(tái),讓音樂可以繼續(xù)播放
#pragma mark - 監(jiān)聽APP進(jìn)入前/后臺(tái)(處理APP進(jìn)入后臺(tái),音樂播放暫停問題)
- (void)registerAllNotifications
{
// 后臺(tái)通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(apllicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
// 進(jìn)入前臺(tái)通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(apllicationWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
#pragma mark - 移除監(jiān)聽
- (void)removeAllNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
}
#pragma mark - 進(jìn)入后臺(tái)通知
- (void)apllicationWillResignActiveNotification:(NSNotification *)n
{
NSError *error = nil;
// 后臺(tái)播放代碼
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:&error];
if(error) {
NSLog(@"ListenPlayView background error0: %@", error.description);
}
//后臺(tái)播放
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
if(error) {
NSLog(@"ListenPlayView background error1: %@", error.description);
}
//開啟后臺(tái)處理多媒體事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
#pragma mark - 進(jìn)入前臺(tái)通知
- (void)apllicationWillEnterForegroundNotification:(NSNotification *)n {
// 進(jìn)前臺(tái) 設(shè)置不接受鎖屏頁面控制
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}
最終結(jié)果
在這里插入圖片描述
demo地址:https://download.csdn.net/download/tianzhilan0/15045404
demo地址:https://github.com/tianzhilan0/TTSBackPlayer