iOS后台持续播放音乐

作者: CGPointZero | 来源:发表于2015-12-10 09:52 被阅读18910次

之前在App Store上架了一个音乐播放器软件,用的是AVPlayer做的音乐播放器。很多用户反映没有后台播放,最近决定更新一下。
注意,重点是持续后台播放,网络歌曲也可以,重点是持续播放,后台播放很简单,但是后台持续播放,则需要做一些处理,申请后台id,才能实现持续播放。

1.开启所需要的后台模式:

选中Targets-->Capabilities-->BackgroundModes-->ON,
并勾选Audio and AirPlay选项,如下图


设置后台模式

2.在Appdelegate.m的applicationWillResignActive:方法中激活后台播放,代码如下:

-(void)applicationWillResignActive:(UIApplication *)application
{
    //开启后台处理多媒体事件
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    AVAudioSession *session=[AVAudioSession sharedInstance];
    [session setActive:YES error:nil];
    //后台播放
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    //这样做,可以在按home键进入后台后 ,播放一段时间,几分钟吧。但是不能持续播放网络歌曲,若需要持续播放网络歌曲,还需要申请后台任务id,具体做法是:
_bgTaskId=[AppDelegate backgroundPlayerID:_bgTaskId];
    //其中的_bgTaskId是后台任务UIBackgroundTaskIdentifier _bgTaskId;
}
实现一下backgroundPlayerID:这个方法:
+(UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId
{
    //设置并激活音频会话类别
    AVAudioSession *session=[AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
    //允许应用程序接收远程控制
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    //设置后台任务ID
    UIBackgroundTaskIdentifier newTaskId=UIBackgroundTaskInvalid;
    newTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    if(newTaskId!=UIBackgroundTaskInvalid&&backTaskId!=UIBackgroundTaskInvalid)
    {
        [[UIApplication sharedApplication] endBackgroundTask:backTaskId];
    }
    return newTaskId;
}

3.处理中断事件,如电话,微信语音等。

原理是,在音乐播放被中断时,暂停播放,在中断完成后,开始播放。具体做法是:
-->在通知中心注册一个事件中断的通知:
//处理中断事件的通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterreption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];

实现接收到中断通知时的方法

//处理中断事件

-(void)handleInterreption:(NSNotification *)sender
{
    if(_played)
    {
      [self.playView.player pause];
        _played=NO;
    }
    else
    {
        [self.playView.player play];
        _played=YES;
    }
}

4.至此音乐后台持续播放搞定,大功告成!现在可以打开软件播放一首歌曲,然后按home键回到后台,音乐会继续播放~

应简友要求,在此贴出播放器代码:

简单看下界面

list player now play info center
  • PlayerViewController.h

#import "RootViewController.h"
#import "songInfoModel.h"
#import <AVFoundation/AVFoundation.h>
#import "PlayView.h"
#import "FMSongListModel.h"

@interface PlayerViewController : RootViewController

//下载链接
@property(nonatomic, strong)NSString        *sourceURLString;
@property(nonatomic, copy)NSString          *songname;
@property(nonatomic, copy)NSString          *singerName;
@property(nonatomic, copy)NSString          *songUrl;
@property(nonatomic, copy)NSString          *songPicUrl;
@property(nonatomic, strong)songInfoModel   *model;
@property(nonatomic,assign)NSInteger        currentSongIndex;
@property(nonatomic,strong)NSArray          *songArray;

+(instancetype)sharedPlayerController;
//刷新播放的歌曲
-(void)refreshWithModel:(songInfoModel *)sim;
//停止播放
-(void)stop;
//暂停
-(void)pauseMusic;

@end
  • PlayerViewController.m

#import "PlayerViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "UIImageView+WebCache.h"
#import "mvListModel.h"
#import "PlayView.h"
#import "Word.h"
#import "auditionListModel.h"
#import "AFNetworking.h"
#import "QFHttpManager.h"
#import "QFHttpRequest.h"
#import <ShareSDK/ShareSDK.h>
#import "Maker.h"
#import <MediaPlayer/MediaPlayer.h>
#import "Constant.h"
#import "PopMenu.h"
#import "KxMenu.h"
#import "FMSongListModel.h"
#import "FMSongModel.h"
#import "MCDataEngine.h"
#import "ProgressHUD.h"
#import "DownloadViewController.h"
#import "DownloadModel.h"
#import "DownloadDBManager.h"
#import "FGGDownloadManager.h"
@interface PlayerViewController ()<UITableViewDataSource, UITableViewDelegate>{
    
    UITableView             *_tbView;
    //分享菜单
    PopMenu *_menu;
    UITapGestureRecognizer  *_hideShareGesture;
    //暂无歌词的提示Label
    UILabel                 *_noLrcLb;
}

@property(nonatomic,assign)MusicPlayType      playType;
//标记分享菜单是否显示
@property(nonatomic,assign)BOOL               isListShow;
//透明背景
@property(nonatomic,strong)UIView             *shareBackView;
@property(nonatomic,copy)NSString             *totalTime;
//当前歌词行
@property(nonatomic,assign)NSInteger          currentWordRow;
@property(nonatomic, strong)UIProgressView    *videoProgress;
@property(nonatomic, strong)UIImageView       *singerImageView;
@property(nonatomic,strong)NSArray            *wordsArray;
@property(nonatomic, strong)AVPlayerItem      *playerItem;
@property(nonatomic, strong)PlayView          *playView;
@property(nonatomic,assign)BOOL               played;
@property(nonatomic, strong)id                playbackTimeObserver;

@end
static PlayerViewController *musicController=nil;

@implementation MusicPicViewController

+(instancetype)sharedPlayerController{
    
    if(!musicController){
        
        musicController=[[PlayerViewController alloc] init];
    }
    return musicController;
}
- (void)viewDidLoad {
    
    [super viewDidLoad];
    [self setUp];
    [self createBackView];
    [self createUI];
    [self createTitleView];
    [self loadLyric];
    [self createShareList];
    [self preload];
    [self load];
}
//初始化
-(void)setUp {
    
    self.view.backgroundColor = [UIColor blackColor];
    self.automaticallyAdjustsScrollViewInsets=NO;
    self.navigationController.navigationBar.barStyle=UIBarStyleBlackOpaque;
    _currentWordRow=0;
    //默认循环播放
    _playType=MusicPlayTypeRound;
}
#pragma mark - 背景图片
-(void)createBackView {
    
    UIImageView * backImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 64, kWidth, kHeight-64-90)];
    backImageView.image = [UIImage returnImageWithName:@"bg.jpg"];
    backImageView.alpha=0.7;
    [self.view addSubview:backImageView];
}
- (void)createUI {
    
    _playView = [[PlayView alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)];
    [self.view addSubview:_playView];
    __weak typeof(self) weakSelf=self;
    _playView.nextBtnDidClickedBlock=^{
        if(weakSelf.playType==MusicPlayTypeRandom)
           [weakSelf randomNext];
        else
            [weakSelf next];
    };
    _playView.previousBtnDidClickedBlock=^{
        if(weakSelf.playType==MusicPlayTypeRandom)
            [weakSelf randomNext];
        else
            [weakSelf previous];
    };
    //切换播放模式
    _playView.typeBtnDidClickedBlock=^(UIButton *sender){
        
        if(weakSelf.playType==MusicPlayTypeRound){
            
            [sender setTitle:@"单曲" forState:UIControlStateNormal];
            //[sender setImage:[UIImage imageNamed:@"single"] forState:UIControlStateNormal];
            weakSelf.playType=MusicPlayTypeSingle;
            [ProgressHUD showSuccess:@"单曲循环"];
            
        }else if(weakSelf.playType==MusicPlayTypeSingle){
            
            [sender setTitle:@"随机" forState:UIControlStateNormal];
            //[sender setImage:[UIImage imageNamed:@"random"] forState:UIControlStateNormal];
            weakSelf.playType=MusicPlayTypeRandom;
            [ProgressHUD showSuccess:@"随机播放"];
            
        }else if(weakSelf.playType==MusicPlayTypeRandom){
            
            [sender setTitle:@"循环" forState:UIControlStateNormal];
            //[sender setImage:[UIImage imageNamed:@"round"] forState:UIControlStateNormal];
            weakSelf.playType=MusicPlayTypeRound;
            [ProgressHUD showSuccess:@"循环播放"];
        }
    };
    [self.playView.slider addTarget:self action:@selector(videoSliderChangeValue:) forControlEvents:UIControlEventValueChanged];
    
    self.videoProgress=[[UIProgressView alloc] initWithFrame:CGRectMake(93, kHeight-48, kWidth-180, 15)];
    _videoProgress.alpha=0.6;
    _videoProgress.progressTintColor=RGB(76, 168, 76);
    [self.view addSubview:_videoProgress];

    [self.playView.playButton addTarget:self action:@selector(onClickChangeState) forControlEvents:UIControlEventTouchUpInside];
    
    UIImageView * singerImageView =[[UIImageView alloc] initWithFrame:CGRectMake(10, kHeight-80, 60, 60)];
    singerImageView.tag=kSingerImageTag;
    self.singerImageView = singerImageView;
    if(self.model.mvListArray) {
        
        mvListModel * model=nil;
        if ([self.model.mvListArray count]){
            
             model= [self.model.mvListArray firstObject];
        }
        singerImageView.image=[UIImage imageNamed:@"defaultNomusic.png"];
        NSURL *url=nil;
        if(model.picUrl.length>0){
            
            url=[NSURL URLWithString:model.picUrl];
        }
        if(url){
            
            [singerImageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"defaultNomusic.png"]];
        }
        
    }else{
        
        NSURL *url=[NSURL URLWithString:self.songPicUrl];
        [singerImageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"defaultNomusic.png"]];
    }
    singerImageView.clipsToBounds = YES;
    singerImageView.layer.cornerRadius = 30;
    [self.view addSubview:singerImageView];
    
    //歌手头像循环滚动
    [self repeadRatation];
    
    _tbView=[[UITableView alloc]initWithFrame:CGRectMake(10, 64, kWidth-20,kHeight-64-90) style:UITableViewStylePlain];
    _tbView.delegate=self;
    _tbView.dataSource=self;
    _tbView.separatorStyle=UITableViewCellSeparatorStyleNone;
    [self.view addSubview:_tbView];
    _tbView.showsVerticalScrollIndicator=NO;
    _tbView.backgroundColor=[UIColor clearColor];
    
    //暂无歌词的label
    _noLrcLb=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 40)];
    _noLrcLb.center=self.view.center;
    _noLrcLb.backgroundColor=[UIColor clearColor];
    _noLrcLb.textAlignment=NSTextAlignmentCenter;
    _noLrcLb.font=[UIFont systemFontOfSize:16];
    _noLrcLb.textColor=[UIColor whiteColor];
    [self.view addSubview:_noLrcLb];
    if(_wordsArray.count>0){
        
        _noLrcLb.text=nil;
        
    }else{
        
        _noLrcLb.text=@"暂无歌词~";
    }
}
-(void)createTitleView{
    
    UILabel *statusLb=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, kWidth, 64)];
    statusLb.backgroundColor=RGB(76, 168, 76);
    [self.view addSubview:statusLb];
    
    UILabel * songlabel = [[UILabel alloc] initWithFrame:CGRectMake(40,15, kWidth-80, 34)];
    songlabel.numberOfLines=2;
    songlabel.tag=kSongNameTag;
    songlabel.text = self.model.name;
    songlabel.font = [UIFont boldSystemFontOfSize:15];
    songlabel.adjustsFontSizeToFitWidth=YES;
    songlabel.textColor = [UIColor whiteColor];
    songlabel.textAlignment = NSTextAlignmentCenter;
    songlabel.backgroundColor=[UIColor clearColor];
    [self.view addSubview:songlabel];
    
    UILabel * singerLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 50, kWidth-80, 10)];
    singerLabel.tag=kSingerNameTag;
    singerLabel.text = self.model.singerName;
    singerLabel.font = [UIFont systemFontOfSize:12];
    singerLabel.textColor = [UIColor whiteColor];
    singerLabel.textAlignment = NSTextAlignmentCenter;
    singerLabel.backgroundColor=[UIColor clearColor];
    [self.view addSubview:singerLabel];
    
    UIButton *menuBtn=[[UIButton alloc]initWithFrame:CGRectMake(kWidth-40, 30, 20, 16)];
    [menuBtn setImage:[UIImage imageNamed:@"list"] forState:UIControlStateNormal];
    [self.view addSubview:menuBtn];
    [menuBtn addTarget:self action:@selector(showMenu:) forControlEvents:UIControlEventTouchUpInside];
    
    UIButton * backButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 20, 30, 30)];
    [backButton setImage:[UIImage returnImageWithName:@"back.png"] forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(onClickBack) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:backButton];
}
#pragma mark - 加载歌词
- (void)loadLyric{
    
    NSString * singerName = self.model.singerName;
    NSString * songName1 = self.model.name;
    NSArray * songNameArray = [songName1 componentsSeparatedByString:@"("];
    NSString * songName = songNameArray[0];
    NSString * lrcURL = [NSString stringWithFormat:SEARCH_LRC_URL, singerName, songName];
    NSString * newLrcURL = [lrcURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [[QFHttpManager sharedManager] addTask:newLrcURL delegate:self action:@selector(requestFinished:)];
}
#pragma mark - 下载歌词
-(void)requestFinished:(QFHttpRequest*)Request{
    
    NSString * str = [[NSString alloc] initWithData:Request.downloadData encoding:NSUTF8StringEncoding];
    NSRange range = [str rangeOfString:@"<lrclink>/data2/lrc"];
    //暂无歌词的情况
    if (!range.length){
        
        _wordsArray=nil;
        [_tbView reloadData];
        _noLrcLb.text=@"暂无歌词~";
        return;
    }
    NSString * string = [str substringFromIndex:range.location];
    NSArray * array = [string componentsSeparatedByString:@">"];
    NSString * laststr = [NSString stringWithFormat:@"http://musicdata.baidu.com%@", array[1]];
    NSArray * lrcArray = [laststr componentsSeparatedByString:@"<"];
    NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:lrcArray[0]]];
    
    NSString * downloadPath = [NSString stringWithFormat:@"%@/%@-%@.lrc", kLrcPath,self.model.singerName,self.model.name];
    AFHTTPRequestOperation * requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    requestOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:downloadPath append:NO];
    [requestOperation start];
    __weak typeof(self) weakSelf=self;
    [requestOperation setCompletionBlock:^{
        
        [weakSelf readFileOfPath:downloadPath];
    }];
}
#pragma mark - 读取歌词
- (void)readFileOfPath:(NSString *)path{
    
    self.wordsArray=[[Word alloc] wordsWithFilePath:path];
    if(_wordsArray.count>0){
        
        _noLrcLb.text=nil;
    }
    [_tbView reloadData];
}
#pragma mark - 创建分享视图
-(void)createShareList {
    
    NSArray *names=@[@"微信",@"微信朋友圈",@"新浪微博",@"QQ",@"QQ空间",@"短信"];
    NSArray *images=@[@"wx",@"wx_friend",@"sina",@"qq",@"qzone",@"sms"];
    NSMutableArray *array=[NSMutableArray array];
    for(int i=0;i<names.count;i++){
        
        MenuItem *item=[MenuItem itemWithTitle:names[i] iconName:images[i]];
        [array addObject:item];
    }
    _menu=[[PopMenu alloc]initWithFrame:CGRectMake(kWidth/2-160, kHeight/2-150, 320, 320) items:array];
    _menu.menuAnimationType=kPopMenuAnimationTypeNetEase;
    _menu.backgroundColor=[UIColor clearColor];
    __weak typeof(self) weakSelf=self;
    _menu.didSelectedItemCompletion=^(MenuItem *item){
        
        [weakSelf selectItem:item];
    };
    _hideShareGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(showSharelist)];
}
#pragma mark - 显示隐藏分享视图
-(void)showSharelist{
    
    [self loadSound];
    if(_isListShow){
        
        if ([[self.view gestureRecognizers] containsObject:_hideShareGesture])
        {
            [self.view removeGestureRecognizer:_hideShareGesture];
        }
        [_menu dismissMenu];
        [_menu performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:0.3];
        _isListShow=NO;
    }
    else{
        
        _isListShow=YES;
        [_menu showMenuAtView:self.view.window];
        [self.view addGestureRecognizer:_hideShareGesture];
    }
}
-(void)loadSound {
    
    //播放音效
    SystemSoundID soundID;
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"open.wav" ofType:nil]],&soundID);
    //播放短音频
    AudioServicesPlaySystemSound(soundID);
    //增加震动效果
    //AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
-(void)preload{
    
    if(self.model.auditionArray){
        
        if(self.model.auditionArray.count>0){
            
            auditionListModel * auModel = [[auditionListModel alloc] init];
            if(self.model.auditionArray.count>0){
                
                auModel = self.model.auditionArray[0];
            }
            if (self.model.auditionArray.count >= 2) {
                
                auModel = self.model.auditionArray[1];
            }
            self.sourceURLString=auModel.url;
        }
        else{
            
            self.sourceURLString=[[NSString stringWithFormat:@"file://%@/%@.mp3",kMusicPath,self.model.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        }
        
    }else{
        
        self.sourceURLString=self.songUrl;
    }
}
-(void)load{
    
    NSURL * playURL=[NSURL URLWithString:self.sourceURLString];
    self.playerItem =[AVPlayerItem playerItemWithURL:playURL];
    //监听status属性
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监听loadedTimeRanges
    [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    self.playView.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    //播放结束 通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
    //处理中断事件的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterreption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
    //处理远程控制
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listeningRemoteControl:) name:kAppDidReceiveRemoteControlNotification object:nil];
    //注册删除已经下载的歌曲的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadingStateDidChanged:) name:kDownloadMusicDidDeleteNotification object:nil];
    //注册歌曲下载完成的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadingStateDidChanged:) name:kMusicDidFinishDownloadingNotification object:nil];
}
- (void)moviePlayDidEnd:(NSNotification *)notification {
    
    __weak typeof(self) weakSelf=self;
    [self.playView.player seekToTime:kCMTimeZero completionHandler:^(BOOL finished){
        
         switch (weakSelf.playType) {
                 
             case MusicPlayTypeRound:
                 
                 [weakSelf next];
                 break;
                 
            case MusicPlayTypeSingle:
                 
                 [weakSelf.playView.player play];
                 break;
                 
            case MusicPlayTypeRandom:
                 
                 [weakSelf randomNext];
                 break;
                 
             default:
                 break;
         }
    }];
}
#pragma mark - 处理中断事件
- (void)handleInterreption:(NSNotification *)sender {
    
    if(_played){
        
      [self.playView.player pause];
        _played=NO;
        
    }else{
        
        [self.playView.player play];
        _played=YES;
    }
}
#pragma mark - 接收到远程控制时执行的方法
-(void)listeningRemoteControl:(NSNotification *)sender{
    
    NSDictionary *dict=sender.userInfo;
    NSInteger order=[[dict objectForKey:@"order"] integerValue];
    switch (order) {
            
        case UIEventSubtypeRemoteControlPause:{
            
            [self onClickChangeState];
            break;
        }
        case UIEventSubtypeRemoteControlPlay:{
            
            [self onClickChangeState];
            break;
        }
        case UIEventSubtypeRemoteControlTogglePlayPause:{
            
            [self onClickChangeState];
            break;
        }
        case UIEventSubtypeRemoteControlNextTrack:{
            
            if(self.playType==MusicPlayTypeRandom){
                
                [self randomNext];
                
            }else{
                
                [self next];
            }
            break;
        }
        case UIEventSubtypeRemoteControlPreviousTrack:{
            
            if(self.playType==MusicPlayTypeRandom){
                
                [self randomNext];
                
            }else{
                
                [self previous];
            }
            break;
        }
        default:
            break;
    }
}
#pragma mark - 播放状按钮
-(void)onClickChangeState {
    
    if(self.model){
        
        [self setNowPlayingInfo];
    }
    if (!_played){
        
        [self.playView.player play];
        self.playView.playButton.selected=YES;
        
    }else{
        
        [self.playView.player pause];
        self.playView.playButton.selected=NO;
    }
    _played = !_played;
}
#pragma mark -设置控制中心正在播放的信息

-(void)setNowPlayingInfo{
    
    NSMutableDictionary *songDict=[NSMutableDictionary dictionary];
    [songDict setObject:self.model.name forKey:MPMediaItemPropertyTitle];
    [songDict setObject:self.model.singerName forKey:MPMediaItemPropertyArtist];
    [songDict setObject:[NSNumber numberWithDouble:CMTimeGetSeconds(self.playerItem.duration)] forKeyedSubscript:MPMediaItemPropertyPlaybackDuration];
    //设置歌曲图片
    MPMediaItemArtwork *imageItem=[[MPMediaItemArtwork alloc]initWithImage:_singerImageView.image];
    [songDict setObject:imageItem forKey:MPMediaItemPropertyArtwork];
    
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songDict];
    [MPNowPlayingInfoCenter defaultCenter];
}
#pragma mark -随机下一首
-(void)randomNext {
    
    if(self.songArray.count==0){
        
        return;
    }
    if((!self.songUrl||self.songUrl.length==0)){
        
        NSInteger songIndex=arc4random()%_songArray.count;
        self.model=[self.songArray objectAtIndex:songIndex];
        [self refreshWithModel:self.model];
        
    }else{
        
        _currentSongIndex=arc4random()%_songArray.count;
        FMSongListModel *lm=[_songArray objectAtIndex:_currentSongIndex];
    
        MCDataEngine * network = [MCDataEngine new];
        __weak typeof(self) weakSelf=self;
        [ProgressHUD show:@"Loading"];
        [network getSongInformationWith:lm.song_id WithCompletionHandler:^(FMSongModel *smodel) {
            
            [ProgressHUD dismiss];
            //获取歌曲信息
            NSString *songName=smodel.songName;
            NSString *songUrl=smodel.songLink;
            NSString *singerName=lm.author;
            songInfoModel *sim=[songInfoModel new];
            sim.name=songName;
            sim.singerName=singerName;
            
            weakSelf.songUrl=songUrl;
            weakSelf.songPicUrl=smodel.songPicBig;
            weakSelf.model=sim;
            [weakSelf refreshWithModel:sim];
            
        } errorHandler:^(NSError *error) {
            
            [ProgressHUD showError:error.localizedDescription];
        }];
    }
}
#pragma mark - 下一首
//播放下一首
-(void)next{
    
    //若为单曲循环,则不切歌
    if(self.playType==MusicPlayTypeSingle){
        
        return;
    }
    if(self.songArray.count==0){
        
        return;
    }
    if((!self.songUrl||self.songUrl.length==0)){
        
        NSInteger songIndex=[self.songArray indexOfObject:self.model];
        if(songIndex<self.songArray.count-1){
            
            self.model=[self.songArray objectAtIndex:songIndex+1];
            
        }else{
            
            self.model=[self.songArray objectAtIndex:0];
        }
        [self refreshWithModel:self.model];
        
    }else{
        
        FMSongListModel *lm=nil;
        if(_currentSongIndex<self.songArray.count-1){
            
            _currentSongIndex++;
            lm=[self.songArray objectAtIndex:_currentSongIndex];
            
        }else{
            
            _currentSongIndex=0;
            lm=[self.songArray objectAtIndex:0];
        }
        MCDataEngine * network = [MCDataEngine new];
        __weak typeof(self) weakSelf=self;
        [network getSongInformationWith:lm.song_id WithCompletionHandler:^(FMSongModel *smodel) {
            
            //获取歌曲信息
            NSString *songName=smodel.songName;
            NSString *songUrl=smodel.songLink;
            NSString *singerName=lm.author;
            songInfoModel *sim=[songInfoModel new];
            sim.name=songName;
            sim.singerName=singerName;
            
            weakSelf.songUrl=songUrl;
            weakSelf.songPicUrl=smodel.songPicBig;
            weakSelf.model=sim;
            [weakSelf refreshWithModel:sim];
            
        } errorHandler:^(NSError *error) {
            
            NSLog(@"%@",error.localizedDescription);
        }];
    }
}
#pragma mark - 上一首
-(void)previous
{
    if(self.songArray.count==0){
        
        return;
    }
    //若为单曲循环,则不切歌
    if(self.playType==MusicPlayTypeSingle){
        
        return;
    }
    if((!self.songUrl||self.songUrl.length==0)){
        
        NSInteger songIndex=[self.songArray indexOfObject:self.model];
        if(songIndex>0){
            
            self.model=[self.songArray objectAtIndex:songIndex-1];
            
        }else{
            
            self.model=self.songArray.lastObject;
        }
        [self refreshWithModel:self.model];
    }
    else{
        
        FMSongListModel *lm=nil;
        if(_currentSongIndex>0){
            
            _currentSongIndex--;
            lm=[self.songArray objectAtIndex:_currentSongIndex];
            
        }else{
            
            _currentSongIndex=self.songArray.count-1;
            lm=self.songArray.lastObject;
        }
        MCDataEngine * network = [MCDataEngine new];
        __weak typeof(self) weakSelf=self;
        [ProgressHUD show:@"Loading"];
        [network getSongInformationWith:lm.song_id WithCompletionHandler:^(FMSongModel *smodel) {
            
            [ProgressHUD dismiss];
            //获取歌曲信息
            NSString *songName=smodel.songName;
            NSString *songUrl=smodel.songLink;
            NSString *singerName=lm.author;
            songInfoModel *sim=[songInfoModel new];
            sim.name=songName;
            sim.singerName=singerName;
            
            weakSelf.songUrl=songUrl;
            weakSelf.songPicUrl=smodel.songPicBig;
            weakSelf.model=sim;
            [weakSelf refreshWithModel:sim];
            
        } errorHandler:^(NSError *error) {
            
            [ProgressHUD showError:error.localizedDescription];
        }];
    }
}
#pragma mark - 刷新播放的歌曲

-(void)refreshWithModel:(songInfoModel *)sim
{
    self.model=sim;
    self.playView.playButton.enabled = NO;
    self.playView.playButton.selected=NO;
    [self.playView.slider setValue:0.0 animated:YES];
    UIProgressView *progress=(UIProgressView *)[self.view viewWithTag:MUSIC_PROGRESS_TAG];
    [progress setProgress:0.0 animated:YES];
    _currentWordRow=0;
    if((!self.songUrl||self.songUrl.length==0)){
        
        mvListModel * model=nil;
        if ([sim.mvListArray count]){
            
            model= [sim.mvListArray firstObject];
        }
        NSURL *url=nil;
        if(model.picUrl.length>0){
            
            url=[NSURL URLWithString:model.picUrl];
        }
        if(url){
            
            UIImageView *smiv=(UIImageView *)[self.view viewWithTag:kSingerImageTag];
            [smiv sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"defaultNomusic.png"]];
        }
        
    }else{
        
        UIImageView *smiv=(UIImageView *)[self.view viewWithTag:kSingerImageTag];
        [smiv sd_setImageWithURL:[NSURL URLWithString:self.songPicUrl] placeholderImage:[UIImage imageNamed:@"defaultNomusic.png"]];
    }
    UILabel *songNameLb=(UILabel *)[self.view viewWithTag:kSongNameTag];
    UILabel *singerNameLb=(UILabel *)[self.view viewWithTag:kSingerNameTag];
    songNameLb.text=sim.name;
    singerNameLb.text=sim.singerName;
    if(!self.songUrl||self.songUrl.length==0){
        
        if(sim.auditionArray.count>0){
            
            auditionListModel * auModel = [[auditionListModel alloc] init];
            if(sim.auditionArray.count>0){
                
                auModel = sim.auditionArray[0];
            }
            if (sim.auditionArray.count >= 2) {
                
                auModel = sim.auditionArray[1];
            }
            self.sourceURLString=auModel.url;
            
        }else{
            
            self.sourceURLString=[[NSString stringWithFormat:@"file://%@/%@.mp3",kMusicPath,sim.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        }
        
    }else{
        
        self.sourceURLString=self.songUrl;
    }
    NSURL * playURL=[NSURL URLWithString:self.sourceURLString];
    //先移除observer
    [self.playerItem removeObserver:self forKeyPath:@"status"];
    [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
    //播放结束 通知
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
    if([self.playView.player observationInfo]){
        
        self.playbackTimeObserver=nil;
    }
    if(self.playerItem){
        
        self.playerItem=nil;
    }
    self.playerItem =[AVPlayerItem playerItemWithURL:playURL];
    //监听status属性
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监听loadedTimeRanges
    [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    //播放结束 通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
    if(self.playView.player){
        
        if(_played){
            
            [self.playView.player pause];
        }
        self.playView.player=nil;
    }
    self.playView.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    //播放结束 通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
    //加载歌词
    [self loadLyric];
}
//接收到下载页面左滑删除的通知时 或 音乐下载完毕时
-(void)downloadingStateDidChanged:(NSNotification *)sender{
    
    //如果播放的列表是下载歌曲列表
    if(self.model.musicURL.length>0){
        
        NSMutableArray *songInfoArray=[NSMutableArray array];
        NSArray *array=[[DownloadDBManager sharedManager] getAllRecords];
        for(DownloadModel *m in array){
            
            if([[FGGDownloadManager shredManager] lastProgress:m.urlString]==1.0){
                
                songInfoModel *s=[songInfoModel new];
                s.name=m.song_name;
                s.singerName=m.singger_name;
                s.musicURL=m.urlString;
                [songInfoArray addObject:s];
            }
        }
        self.songArray=songInfoArray;
    }
}
#pragma mark - 点击分享项目时
-(void)selectItem:(MenuItem *)item{
    
    ShareType type;
    switch (item.index) {
            
        case 0:
            
            type=ShareTypeWeixiSession;
            break;
            
        case 1:
            
            type=ShareTypeWeixiTimeline;
            break;
            
        case 2:
            
            type=ShareTypeSinaWeibo;
            break;
            
        case 3:
            
            type=ShareTypeQQ;
            break;
            
        case 4:
            
            type=ShareTypeQQSpace;
            break;
            
        case 5:
            
            type=ShareTypeSMS;
            break;
            
        default:
            break;
    }
    NSString *contentString=@"在线听歌看MV,快来试试吧!";
    NSString *imgPath=[[NSBundle mainBundle] pathForResource:@"icon-share" ofType:@"png"];
    NSString *title=[NSString stringWithFormat:@"歌曲-%@",self.model.name];
    NSString *shareURLString;
    if([self.sourceURLString hasPrefix:@"file:"]){
        
        shareURLString=self.model.musicURL;
        
    }else{
        
        shareURLString=self.sourceURLString;
    }
    
    id<ISSContent> pushContent=[ShareSDK content:contentString
                                  defaultContent:contentString
                                           image:[ShareSDK imageWithPath:imgPath]
                                           title:title
                                             url:shareURLString
                                     description:contentString
                                       mediaType:SSPublishContentMediaTypeMusic];
    
    [ShareSDK showShareViewWithType:type
                          container:nil
                            content:pushContent
                      statusBarTips:YES
                        authOptions:nil
                       shareOptions:nil
                             result:^(ShareType type, SSResponseState state, id<ISSPlatformShareInfo> statusInfo, id<ICMErrorInfo> error, BOOL end) {
                                 
                                 if (state==SSResponseStateSuccess){
                                     
                                     [ProgressHUD showSuccess:@"分享成功"];
                                     
                                 }else if (state==SSResponseStateFail){
                                     
                                     NSString *errorMessage=[error errorDescription];
                                     UIAlertView *a=[[UIAlertView alloc]initWithTitle:@"错误" message:errorMessage delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                                     [a show];
                                 }
                             }];
    //隐藏分享列表
    [self showSharelist];
}
//移动slider值变化
- (void)videoSliderChangeValue:(UISlider *)slider{
    
    if(self.playView.player.status!=AVPlayerStatusReadyToPlay){
        
        return;
    }
    CMTime changedTime = CMTimeMakeWithSeconds(slider.value, 1);
    __weak typeof(self) weakSelf=self;
    [self.playView.player seekToTime:changedTime completionHandler:^(BOOL finished) {
        
        [weakSelf.playView.player play];
        weakSelf.playView.playButton.selected = YES;
        weakSelf.played = YES;
    }];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    AVPlayerItem * playerItem = (AVPlayerItem *)object;
    if ([keyPath isEqualToString:@"status"]){
        
        if ([playerItem status] == AVPlayerStatusReadyToPlay){
            
            self.playView.playButton.enabled = YES;
            self.playView.playButton.selected=YES;
            [self.playView.player play];
            //设置正在播放信息
            [self setNowPlayingInfo];
            _played=YES;
            
            //获取音乐总长度
            CMTime duration = self.playerItem.duration;
            self.playView.slider.maximumValue = CMTimeGetSeconds(duration);
            
            //转换成秒
            CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒
            _totalTime = [self convertTime:totalSecond];
            
            // 监听播放状态
            [self monitoringPlayback:self.playerItem];
            
        }else if([playerItem status] == AVPlayerStatusFailed){
            
            [ProgressHUD showError:@"加载失败"];
        }
        
    }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){
        
        NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度
        CMTime duration = self.playerItem.duration;
        CGFloat totalDuration = CMTimeGetSeconds(duration);
        [self.videoProgress setProgress:timeInterval/totalDuration animated:YES];
    }
}
- (NSTimeInterval)availableDuration{
    
    NSArray *loadedTimeRanges = [[self.playView.player currentItem] loadedTimeRanges];
    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
    float startSeconds = CMTimeGetSeconds(timeRange.start);
    float durationSeconds = CMTimeGetSeconds(timeRange.duration);
    NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度
    return result;
}
- (void)monitoringPlayback:(AVPlayerItem *)playerItem{
    
    __weak typeof(self) weakSelf=self;
    
    if(self.playbackTimeObserver){
        
        self.playbackTimeObserver=nil;
    }
    self.playbackTimeObserver = [self.playView.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time){
        
        CGFloat currentSecond = playerItem.currentTime.value/playerItem.currentTime.timescale;// 计算当前在第几秒
        [weakSelf updateVideoSlider:currentSecond];
        
        //更新控制中心歌曲的当前时间
        NSDictionary *info=[[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo];
        NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithDictionary:info];
        [dict setObject:@(currentSecond) forKeyedSubscript:MPNowPlayingInfoPropertyElapsedPlaybackTime];
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
        
        NSString *timeString = [weakSelf convertTime:currentSecond];
        UILabel * timeLabel = (id)[weakSelf.view viewWithTag:TIME_LABEL_TAG];
        timeLabel.text = [NSString stringWithFormat:@"%@/%@",timeString,weakSelf.totalTime];
    }];
}
- (NSString *)convertTime:(CGFloat)second{
    
    NSDate * d = [NSDate dateWithTimeIntervalSince1970:second];
    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    if (second/3600>=1){
        
        [formatter setDateFormat:@"HH:mm:ss"];
        
    }else{
        
        [formatter setDateFormat:@"mm:ss"];
    }
    NSString * showtimeNew = [formatter stringFromDate:d];
    return showtimeNew;
}
#pragma mark - TableView
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    return _wordsArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    static NSString *cid=@"cid";
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cid];
    if(!cell){
        
        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cid];
    }
    cell.backgroundColor=[UIColor clearColor];
    cell.selectionStyle=UITableViewCellSelectionStyleNone;
    Word *word=_wordsArray[indexPath.row];
    cell.textLabel.text=word.text;
    cell.textLabel.font=[UIFont systemFontOfSize:14];
    cell.textLabel.numberOfLines=0;
    cell.textLabel.textAlignment=NSTextAlignmentCenter;
    cell.textLabel.alpha=0.6;
    cell.textLabel.textColor=[UIColor whiteColor];
    if(indexPath.row==_currentWordRow){
        
        cell.textLabel.textColor=[UIColor yellowColor];
        cell.textLabel.font=[UIFont systemFontOfSize:16];
    }
    return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    return 40;
}
//离开页面时,隐藏播放列表
-(void)viewWillDisappear:(BOOL)animated{
    
    [super viewWillDisappear:animated];
    if(_isListShow){
        
        [self showSharelist];
    }
}
-(BOOL)prefersStatusBarHidden{
    
    return YES;
}
#pragma mark - 歌手头像循环滚动
-(void)repeadRatation{
    
    [UIView beginAnimations:@"animation" context:nil];
    [UIView setAnimationDuration:4];
    [UIView setAnimationDelegate:self];
    //循环调用,可以让让动画无限循环
    [UIView setAnimationDidStopSelector:@selector(repeadRatation)];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    _singerImageView.transform=CGAffineTransformRotate(_singerImageView.transform, M_PI);
    [UIView commitAnimations];
}
//时间走动设置
- (void)updateVideoSlider:(CGFloat)currentSecond{
    
    [self.playView.slider setValue:currentSecond animated:YES];
    //歌词同步
    [self setCurrentWordWithTime:currentSecond];
}

#pragma mark - 设置歌词滚动
-(void)setCurrentWordWithTime:(CGFloat)currentTime {
    
    if(_wordsArray.count>0){
        
        for(int i=0;i<=_wordsArray.count-1;i++){
            
            Word *rear=_wordsArray[i];
            if(currentTime>=rear.timeLength){
                
                _currentWordRow=i;
                [_tbView reloadData];
                [_tbView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_currentWordRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
            }
        }
    }
}
#pragma mark - 停止播放
-(void)stop{
    
    self.playView.playButton.selected = NO;
    [[QFHttpManager sharedManager] removeAllTask:self];
    //移除观察者及通知
    [self.playerItem removeObserver:self forKeyPath:@"status" context:nil];
    [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];
    //播放结束
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
    //处理远程控制
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kAppDidReceiveRemoteControlNotification object:nil];
    //中断通知注销
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
    //删除下载歌曲通知注销
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kDownloadMusicDidDeleteNotification object:nil];
    //注销音乐下载完成的通知
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kMusicDidFinishDownloadingNotification object:nil];
    
    if([self.playView.player observationInfo]){
        
        self.playbackTimeObserver=nil;
    }
    if(_played){
        
        [self.playView.player pause];
    }
    self.playView.player = nil;
    self.playerItem=nil;
    if(musicController){
        
        musicController=nil;
    }
}
#pragma mark - 暂停
-(void)pauseMusic{
    
    if(_played){
        
        [self.playView.player pause];
        self.playView.playButton.selected=NO;
        _played = NO;
        
    }else{
        
        if(self.model){
            
            [self.playView.player play];
            self.playView.playButton.selected=YES;
            _played =YES;
        }
    }
}
  • PlayerView.h

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface PlayView : UIView

@property (nonatomic, strong) UIButton * playButton;
@property (nonatomic, strong) AVPlayer * player;
@property (nonatomic, strong) UISlider * slider;
//@property (nonatomic, strong) UIProgressView *videoProgress;
@property(nonatomic,copy)void (^nextBtnDidClickedBlock)();
@property(nonatomic,copy)void (^previousBtnDidClickedBlock)();
@property(nonatomic,copy)void (^typeBtnDidClickedBlock)(UIButton *sender);

@end
  • PlayerView.m

#import "PlayView.h"
#import "Constant.h"

@implementation PlayView

- (instancetype)initWithFrame:(CGRect)frame{
    
    if (self = [super initWithFrame:frame]){
        
        UIView *backView=[[UIView alloc] initWithFrame:CGRectMake(0, kHeight-90, kWidth, 90)];
        backView.backgroundColor=RGB(76, 168, 76);
        [self addSubview:backView];
        
        //播放按钮
        self.playButton = [[UIButton alloc] initWithFrame:CGRectMake(kWidth/2-12.5,  kHeight-38, 25, 25)];
        _playButton.tag = PLAYSTOP_BUTTON_TAG;
        [_playButton setImage:[UIImage imageNamed:@"GplayButton"] forState:UIControlStateNormal];
        [_playButton setImage:[UIImage imageNamed:@"Pause1"] forState:UIControlStateSelected];
        [self addSubview:_playButton];
        
        //上一首
        UIButton *previousBtn=[[UIButton alloc]initWithFrame:CGRectMake(CGRectGetMinX(_playButton.frame)-50, kHeight-38, 25, 25)];
        [previousBtn setImage:[UIImage imageNamed:@"stepbackward"] forState:UIControlStateNormal];
        [self addSubview:previousBtn];
        [previousBtn addTarget:self action:@selector(previousAction) forControlEvents:UIControlEventTouchUpInside];
        
        //下一首
        UIButton *nextBtn=[[UIButton alloc]initWithFrame:CGRectMake(CGRectGetMaxX(_playButton.frame)+25, kHeight-38, 25, 25)];
        [nextBtn setImage:[UIImage imageNamed:@"stepforward"] forState:UIControlStateNormal];
        [self addSubview:nextBtn];
        [nextBtn addTarget:self action:@selector(nextAction) forControlEvents:UIControlEventTouchUpInside];
        //滑动条
        self.slider=[[UISlider alloc] initWithFrame:CGRectMake(91, kHeight-55, kWidth-176, 15)];
        [_slider setMinimumTrackTintColor:RGB(196, 192, 192)];
        [_slider setMaximumTrackTintColor:[UIColor whiteColor]];
        
        [_slider setThumbImage:[UIImage imageNamed:@"player-progress-point-h"] forState:UIControlStateNormal];
        [self addSubview:self.slider];
        
        //时间
        UILabel * timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(kWidth/2-50, kHeight-80, 100, 20)];
        timeLabel.font=[UIFont systemFontOfSize:14];
        timeLabel.textAlignment=NSTextAlignmentCenter;
        timeLabel.text = @"00:00/00:00";
        timeLabel.adjustsFontSizeToFitWidth=YES;
        timeLabel.textColor = [UIColor whiteColor];
        timeLabel.tag = TIME_LABEL_TAG;
        [self addSubview:timeLabel];
        
        //播放模式切换按钮
        UIButton *typeBtn=[[UIButton alloc]initWithFrame:CGRectMake(kWidth-60, kHeight-60, 40, 30)];
        [typeBtn setTitle:@"循环" forState:UIControlStateNormal];
        typeBtn.titleLabel.font=[UIFont boldSystemFontOfSize:14];
        [typeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [self addSubview:typeBtn];
        [typeBtn addTarget:self action:@selector(typeTroggleAction:) forControlEvents:UIControlEventTouchUpInside];
    }
    return self;
}
//上一首
-(void)previousAction{
    
    if(self.previousBtnDidClickedBlock){
        
        self.previousBtnDidClickedBlock();
    }
}
//下一首
-(void)nextAction{
    
    if(self.nextBtnDidClickedBlock){
        
        self.nextBtnDidClickedBlock();
    }
}
//模式切换
-(void)typeTroggleAction:(UIButton *)sender{
    
    if(self.typeBtnDidClickedBlock){
        
        self.typeBtnDidClickedBlock(sender);
    }
}
@end
  • AppDelegate.m

#import "AppDelegate.h"
#import "ReadingViewController.h"
#import "RelatedShareSDK.h"

#import <ShareSDK/ShareSDK.h>
#import <TencentOpenAPI/QQApiInterface.h>
#import <TencentOpenAPI/TencentOAuth.h>
#import "WXApi.h"
#import "WeiboSDK.h"

#import <AVFoundation/AVFoundation.h>

#import "PlayerViewController.h"
#import "QFHttpManager.h"

#import "LeftViewController.h"
#import "Constant.h"
#import "CustomTabBarController.h"
#import "FGGDownloadManager.h"

@interface AppDelegate ()
{
    //后台播放任务Id
    UIBackgroundTaskIdentifier _bgTaskId;
}
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [self setUp];
    
    [ShareSDK registerApp:Appkey_ShareSDK];
    [self initilizePlatform];
    
    CustomTabBarController *tabBar=[[CustomTabBarController alloc]init];
    LeftViewController *left=[[LeftViewController alloc]init];
    _sideMenu=[[RESideMenu alloc]initWithContentViewController:tabBar leftMenuViewController:left rightMenuViewController:nil];
    
    self.window.rootViewController=_sideMenu;
    
    //开启后台处理多媒体事件
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    
    return YES;
}
//初始化
-(void)setUp
{
    //移动资源文件
    NSString *dbPath = [docPath stringByAppendingPathComponent:@"FreeMusic.db"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath])
    {
        NSString *srcPath = [[NSBundle mainBundle] pathForResource:@"FreeMusic" ofType:@"db"];
        [[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:dbPath error:nil];
    }
    //创建歌词文件夹
    if(![[NSFileManager defaultManager] fileExistsAtPath:kLrcPath])
    {
        [[NSFileManager defaultManager] createDirectoryAtPath:kLrcPath withIntermediateDirectories:YES attributes:nil error:nil];
    }
    //创建歌曲下载文件夹
    if(![[NSFileManager defaultManager] fileExistsAtPath:kMusicPath])
    {
        [[NSFileManager defaultManager]createDirectoryAtPath:kMusicPath withIntermediateDirectories:YES attributes:nil error:nil];
    }
}
-(void)initilizePlatform
{
    /**
     连接微信应用以使用相关功能,此应用需要引用WeChatConnection.framework和微信官方SDK
     http://open.weixin.qq.com上注册应用,并将相关信息填写以下字段
     **/
    [ShareSDK connectWeChatWithAppId:kWXAppkey
                           appSecret:kWXSecret
                           wechatCls:[WXApi class]];
    //微信朋友圈
    [ShareSDK connectWeChatTimelineWithAppId:kWXAppkey
                                   appSecret:kWXSecret
                                   wechatCls:[WXApi class]];
    /**
     连接新浪微博开放平台应用以使用相关功能,此应用需要引用SinaWeiboConnection.framework
     http://open.weibo.com上注册新浪微博开放平台应用,并将相关信息填写到以下字段
     **/
    [ShareSDK connectSinaWeiboWithAppKey:kWeiboAppkey
                               appSecret:kWeiboSecret
                             redirectUri:kSinaURI];
    /**
     连接QQ应用以使用相关功能,此应用需要引用QQConnection.framework和QQApi.framework库
     http://mobile.qq.com/api/上注册应用,并将相关信息填写到以下字段
     **/
    [ShareSDK connectQQWithQZoneAppKey:kQQAppkey
                     qqApiInterfaceCls:[QQApiInterface class]
                       tencentOAuthCls:[TencentOAuth class]];
    /**
     连接QQ空间应用以使用相关功能,此应用需要引用QZoneConnection.framework
     http://connect.qq.com/intro/login/上申请加入QQ登录,并将相关信息填写到以下字段
     
     如果需要实现SSO,需要导入TencentOpenAPI.framework,并引入QQApiInterface.h和TencentOAuth.h,将QQApiInterface和TencentOAuth的类型传入接口
     **/
    [ShareSDK connectQZoneWithAppKey:kQQAppkey
                           appSecret:kQQSecret
                   qqApiInterfaceCls:[QQApiInterface class]
                     tencentOAuthCls:[TencentOAuth class]];
    
    //短信
//    [ShareSDK connectSMS];     
}
- (BOOL)application:(UIApplication *)application
      handleOpenURL:(NSURL *)url
{
    return [ShareSDK handleOpenURL:url
                        wxDelegate:self];
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation
{
    return [ShareSDK handleOpenURL:url
                 sourceApplication:sourceApplication
                        annotation:annotation
                        wxDelegate:self];
}

-(void)applicationWillResignActive:(UIApplication *)application
{
    AVAudioSession *session=[AVAudioSession sharedInstance];
    [session setActive:YES error:nil];
    //后台播放
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    _bgTaskId=[AppDelegate backgroundPlayerID:_bgTaskId];
}
+(UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId
{
    //设置并激活音频会话类别
    AVAudioSession *session=[AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
    //允许应用程序接收远程控制
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    //设置后台任务ID
    UIBackgroundTaskIdentifier newTaskId=UIBackgroundTaskInvalid;
    newTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    if(newTaskId!=UIBackgroundTaskInvalid&&backTaskId!=UIBackgroundTaskInvalid)
    {
        [[UIApplication sharedApplication] endBackgroundTask:backTaskId];
    }
    return newTaskId;
}
#pragma mark - 接收到远程控制事件

-(void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    if(event.type==UIEventTypeRemoteControl)
    {
        NSInteger order=-1;
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPause:
                order=UIEventSubtypeRemoteControlPause;
                break;
            case UIEventSubtypeRemoteControlPlay:
                order=UIEventSubtypeRemoteControlPlay;
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                order=UIEventSubtypeRemoteControlNextTrack;
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                order=UIEventSubtypeRemoteControlPreviousTrack;
                break;
            case UIEventSubtypeRemoteControlTogglePlayPause:
                order=UIEventSubtypeRemoteControlTogglePlayPause;
                break;
            default:
                order=-1;
                break;
        }
        NSDictionary *orderDict=@{@"order":@(order)};
        //发送通知
        [[NSNotificationCenter defaultCenter] postNotificationName:kAppDidReceiveRemoteControlNotification object:nil userInfo:orderDict];
    }
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}

- (void)applicationWillEnterForeground:(UIApplication *)application {

}

- (void)applicationDidBecomeActive:(UIApplication *)application {

}
- (void)applicationWillTerminate:(UIApplication *)application {
    
    PlayerViewController *music=[PlayerViewController sharedPlayerController];
    [music stop];
    //取消所有下载任务
    [[FGGDownloadManager shredManager] cancelAllTasks];
}
@end

相关文章

  • iOS 后台运行音乐必看文章

    iOS后台运行之后台播放音乐 必看参考文章:后台播放音乐 后台播放核心代码 电话中断后继续播放核心代码

  • iOS后台持续播放音乐

    之前在App Store上架了一个音乐播放器软件,用的是AVPlayer做的音乐播放器。很多用户反映没有后台播放,...

  • iOS后台持续播放音乐

    前言 需要实现像闹钟那样通知过后然后不进入app直接播放音乐的功能持续后台播放,网络歌曲也可以,重点是持续播放,后...

  • iOS后台持续播放音乐

    http://www.jianshu.com/p/ab300ea6e90c

  • iOS后台持续播放音乐以及来电打断等

    之前做的app需求后台播放音乐,并且音乐需要持续播放下去,做这个的时候碰到几个坑: 1、app进入后台后音乐播放一...

  • ios-后台播放视频、直播流

    关于ios-后台播放 后台播放的不是新的技术;后台播放在音乐播放器上得到了广泛的运用;想了解和实现的原因:看到B站...

  • Paper Collection - Background Ta

    1.IOS后台运行机制详解(一)2.IOS后台运行机制详解(二)3.IOS后台运行 之 后台播放音乐4.转载:IO...

  • iOS应用程序在后台保持运行实现方式

    在iOS系统上应用程序在后台如何长时间运行 播放音乐 声明Audio 获取GPS信号 iOS 长时间后台的两种方法...

  • iOS - 后台保活(后台持续运行代码)

    iOS有两种后台运行保活方式,第一种叫无声音乐保活(即在后台开启音频播放,只不过不需要播放出音量且不能影响其他音乐...

  • 后台相关

    1、后台播放2、锁屏界面展示,这个要真机上才可以 iOS模拟器中播放音乐退到后台还是有用的,但是真机不行,要做如下...

网友评论

  • 冰三尺:请问下, 我在锁屏状态下, 暂停歌曲, 进入App 点击播放, 发现没有声音了, 歌曲确实在播放, 进度时间也正确, 就是没声音? 这个楼主遇到过吗?
  • PGOne爱吃饺子:我的妈啊,这个博客三年了啊!!!
  • b8a07babf83e:后台播放点击暂停时 进度条跟时间会归零 。 大神有办法解决吗
    PGOne爱吃饺子:你好,可以问你一个问题么?别拒绝我啊
    CGPointZero:@酷炫的我 好的,我有空看看
  • 小苏打啦啦啦:Home键后播放还是停止了,有Demo吗?
    CGPointZero:@小苏打12138 可以播放的,应该是你哪里弄错了
    你去app store下一个“音乐榜”,里面虽然很多接口都无法访问了,但是第二个tab“歌手榜”,随便选个歌手,随便播放一首歌,然后按home键,是可以正常播放的。而这个app的代码就是本文的代码播放器部分。
  • 飞鱼_9cc9:锁屏能不能不显示播放面板?在线等~~,急!!
    PGOne爱吃饺子:@飞鱼_9cc9 大哥,程序在后台的时候,可以把音乐播放面板隐藏掉么
    飞鱼_9cc9:@CGPointZero 好的,已经找到了,谢谢!
    CGPointZero:@飞鱼_9cc9 你对着看下,我受伤了,很久没有维护了,抱歉啊
  • Sam_xing:当我锁屏播放音频播放的时候,点击暂停后(此时会切换成播放按钮),在切换下一首歌曲.暂停按钮不能复原,进度条也不更新.请问一下,是否遇见过这个问题?怎么解决.?
  • 呵呵哈哈嘿嘿:请教下,_bgTaskId从哪儿来?
    Biharry:自己添加一个属性
  • MrCoolHao:楼主你好,我按照你方法实现了后台持续播放及后台切换音乐等,非常感谢,有个问题就是,当我后台点击暂停,控制面板上那个进度条和播放时间会恢复初始状态0,我再点击播放,歌曲是从暂停状态继续播放,而进度条和当前播放时间是从0开始,我该如何操作,才能使进度条与播放状态保持一致?期待楼主的指导
    CGPointZero:@MrCoolHao 歌曲回恢复播放的时候,刷新下播放中心的信息
  • 84f7637a4e26:自己写的老是不通,有demo参考吗?求demo:pray:
  • KeyboardLife:我中断事件结束以后,播放器又从头开始播放,不知道啥情况??我拖拽进度条播放,在点击微信语音,中断回来以后可以继续以前的时间播放。
  • 28bb64fffadd:您好,请问下,按照宁注册通知,电话打断还是不能回复播放呢,问下怎么解决呢
  • Sparkle_S:请问,如何隐藏"暂停,上一首,下一首"三个按钮?
    PGOne爱吃饺子:@Sparkle_S 不能隐藏播放面板啊
    Sparkle_S:@PGOne爱吃饺子 隐藏了上一首和下一首
    PGOne爱吃饺子:我也想隐藏,你隐藏了么
  • L泽:谢谢分享
  • ce23359e895d:后台连续播放那个帮大忙了。。谢谢
    CGPointZero:@shier_623 不谢
  • fbc93a1b23f4:您好,可否问一下,为何锁屏后播放Apple Music里的音乐可以继续,而播放本地沙盒目录下的音频大概二十秒后就会没声音?
  • f1880967bb87:按了Home键立马就停止播放,锁屏可以继续播放 请问是为啥呢?
    CGPointZero:static MusicPicViewController *musicController=nil;

    @Implementation MusicPicViewController

    +(instancetype)sharedPlayerController{

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

    musicController=[[MusicPicViewController alloc] init];
    });
    return musicController;
    }
    CGPointZero:@MR_Charles丶 你的播放器界面做成了单例吗?或者播放器做成单例了吗?
  • 不董_:我想问下楼主,后台播放,怎么才能把锁屏之后按home键手机显示的音乐播放器控制界面隐藏掉?
    CGPointZero:@不董_
    #pragma mark -设置控制中心正在播放的信息

    -(void)setNowPlayingInfo
    {
    NSMutableDictionary *songDict=[NSMutableDictionary dictionary];
    [songDict setObject:self.model.name forKey:MPMediaItemPropertyTitle];
    [songDict setObject:self.model.singerName forKey:MPMediaItemPropertyArtist];
    [songDict setObject:[NSNumber numberWithDouble:CMTimeGetSeconds(self.playerItem.duration)] forKeyedSubscript:MPMediaItemPropertyPlaybackDuration];
    //设置歌曲图片
    MPMediaItemArtwork *imageItem=[[MPMediaItemArtwork alloc]initWithImage:_singerImageView.image];
    [songDict setObject:imageItem forKey:MPMediaItemPropertyArtwork];

    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songDict];
    [MPNowPlayingInfoCenter defaultCenter];
    }
    不董_:@CGPointZero 劳烦指点一下NowPlayCenter的注册在哪里?我试了没找到
    CGPointZero:@不董_ 去掉NowPlayCenter的注册
  • 阿龍飛:老哥:iOS播放音乐被来电打断怎么恢复播放
    阿龍飛:@CGPointZero 不行啊
    阿龍飛:@CGPointZero 在后台播放如何恢复
    CGPointZero:@阿龍飛 注册中断事件的通知,在中断事件结束后,恢复播放,我帖子里有
  • c4ibD3:没有Demo吗??
  • 5e26ed09492b:有demo么,想看一下源码
  • 来宝:楼主,我测试的结果是,应用播放音乐后马上按锁屏按钮音乐就会停止,如果应用播放音乐后先按home键进入后台再按锁屏按钮就不会停止,这个怎么解决?
    youth杨:@来宝 我的也遇到了锁屏后音乐停止的情况,请问你是怎么解决的
    来宝:@CGPointZero 已经解决了
    CGPointZero:@来宝 我的没有这个bug
  • 滚来滚去的桔子:在 ios9上能后台,ios10有时能,有时不能,这怎么解决?
  • hard_man:用了你的代码,效果非常赞!就是不知道能不能过审。
    CGPointZero:@hard_man 只要你的音乐有版权,一定能审核通过,我的音乐软件已经通过了
  • 6357a8a55db3:不可以吧
  • 音乐壁纸:比如我在一个控制器播放音乐,然后返回上一层控制器,当前控制器被销毁,然后音乐也停止播放了,这个怎么解决呢
    李徐安:老哥 我也是这个需求。但弄不好。能不呢个你的单利播放类 发我下行不 0.0
    音乐壁纸:@人无再少年_磊子 已解决! 我把播放器封装成一个单例播放类,就可以在整个app播放了。QQ群:98787555
  • plu:楼主求教,按照这个步骤做了之后在后台还是不可以继续播放呢?
    CGPointZero:@plu 可以
    plu:@CGPointZero AVPlayer可以播放网络请求下来的MP3格式的么?
    CGPointZero:@plu 不会吧,这个我亲自验证过的,你看看是不是你哪里写错了
  • 水_水:如何后台播放了按暂停了,等10分钟后后台再按播放按钮可以继续播放么?
  • 哈么么茶:怎么实现灭屏再开的时候 播放三个按钮的控制?
    CGPointZero:@哈么么茶 设置NowPlayInfo
  • 张云龙:请问如何解决使用AVPlayer播放时,手机静音,播放的音频或者视频没有声音的问题?
    刘小壮:龙神,好巧,又碰到你了。
    Onlyoner://在手机的静音模式下也播放声音
    AVAudioSession *a = [AVAudioSession sharedInstance];
    [a setCategory:AVAudioSessionCategoryPlayback error:nil];
  • Evens_Book:请问下楼主,后台播放是可以的,如何让一首歌曲后台播放完成后,自动切换下一曲呢
    CGPointZero:@Evens_Book 自己写一个方法,在播放完成的通知中,调用下一首这个方法就OK
  • 阿超LOVE:学习了

本文标题:iOS后台持续播放音乐

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