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
    

    相关文章

      网友评论

      • 冰三尺:请问下, 我在锁屏状态下, 暂停歌曲, 进入App 点击播放, 发现没有声音了, 歌曲确实在播放, 进度时间也正确, 就是没声音? 这个楼主遇到过吗?
      • PGOne爱吃饺子:我的妈啊,这个博客三年了啊!!!
      • b8a07babf83e:后台播放点击暂停时 进度条跟时间会归零 。 大神有办法解决吗
        PGOne爱吃饺子:你好,可以问你一个问题么?别拒绝我啊
        CGPointZero:@酷炫的我 好的,我有空看看
      • 小苏打12138: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 不谢
      • Hello_IkeBan:您好,可否问一下,为何锁屏后播放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