ios音樂后臺播放和遠(yuǎn)程控制

最近在做音樂播放器巷折,要靜音狀態(tài)下播放,后臺播放崖咨,遠(yuǎn)程控制锻拘,鎖屏顯示,上拉菜單控制击蹲,來電中斷處理等署拟。

開啟后臺模式:
在TARGETS-Capabilities-Background Modes 勾選Audio,AirPay and Picture in Picture

在AppDelegate中

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //靜音狀態(tài)下播放
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    //處理電話打進(jìn)時中斷音樂播放
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interruptionNotificationHandler:) name:AVAudioSessionInterruptionNotification object:nil];
    //后臺播放
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

    // 在App啟動后開啟遠(yuǎn)程控制事件, 接收來自鎖屏界面和上拉菜單的控制
    [application beginReceivingRemoteControlEvents];
    // 處理遠(yuǎn)程控制事件
    [self remoteControlEventHandler];
    
    self.window= [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.window makeKeyAndVisible];
    
    MusicListTableVC * vc = [[MusicListTableViewController alloc] init];
    UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:vc];
    self.window.rootViewController = nc;
    
    return YES;
}

來電中斷處理:

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

    if (interuptionType == AVAudioSessionInterruptionTypeBegan) {
        //獲取中斷前音樂是否在播放
        _played = [MusicPlayViewController shareMusicPlay].isPlaying;
        NSLog(@"AVAudioSessionInterruptionTypeBegan");
    }else if (interuptionType == AVAudioSessionInterruptionTypeEnded) {
        NSLog(@"AVAudioSessionInterruptionTypeEnded");
    }
    
    if(_played)
    {
        //停止播放的事件
        [[MusicPlayTools shareMusicPlay] musicPause];
        _played=NO;
    }else {
        //繼續(xù)播放的事件
        [[MusicPlayTools shareMusicPlay] musicPlay];
        _played=YES;
    }
}
- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"要掛起了。歌豺。推穷。");
    //更新鎖屏信息
    [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // 在App要終止前結(jié)束接收遠(yuǎn)程控制事件, 也可以在需要終止時調(diào)用該方法終止
    [application endReceivingRemoteControlEvents];
}

遠(yuǎn)程控制事件處理:

// 在需要處理遠(yuǎn)程控制事件的具體控制器或其它類中實現(xiàn)
- (void)remoteControlEventHandler
{
    // 直接使用sharedCommandCenter來獲取MPRemoteCommandCenter的shared實例
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    // 啟用播放命令 (鎖屏界面和上拉快捷功能菜單處的播放按鈕觸發(fā)的命令)
    commandCenter.playCommand.enabled = YES;
    // 為播放命令添加響應(yīng)事件, 在點擊后觸發(fā)
    [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        [[MusicPlayTools shareMusicPlay] musicPlay];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    // 播放, 暫停, 上下曲的命令默認(rèn)都是啟用狀態(tài), 即enabled默認(rèn)為YES
    [commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        //點擊了暫停
        [[MusicPlayTools shareMusicPlay] musicPause];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    [commandCenter.previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        //點擊了上一首
        [[MusicPlayViewController shareMusicPlay] lastSongAction];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    [commandCenter.nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        //點擊了下一首
        [[MusicPlayViewController shareMusicPlay] nextSongButtonAction:nil];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
    // 啟用耳機(jī)的播放/暫停命令 (耳機(jī)上的播放按鈕觸發(fā)的命令)
    commandCenter.togglePlayPauseCommand.enabled = YES;
    // 為耳機(jī)的按鈕操作添加相關(guān)的響應(yīng)事件
    [commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        // 進(jìn)行播放/暫停的相關(guān)操作 (耳機(jī)的播放/暫停按鈕)
        [[MusicPlayViewController shareMusicPlay] playPauseButtonAction:nil];
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
        return MPRemoteCommandHandlerStatusSuccess;
    }];
}

鎖頻界面上所顯示的歌曲播放信息和圖片,控制中心上顯示的歌曲播放信息等类咧,這些信息的顯示都由MPNowPlayingInfoCenter類來控制馒铃。
首先#import <MediaPlayer/MPNowPlayingInfoCenter.h>然后調(diào)用MPNowPlayingInfoCenter的單例方法獲取實例,再把需要顯示的信息組織成Dictionary并賦值給nowPlayingInfo屬性就完成了痕惋。

其中常用的是MPNowPlayingInfoPropertyElapsedPlaybackTime和MPNowPlayingInfoPropertyPlaybackRate:

  • MPNowPlayingInfoPropertyElapsedPlaybackTime表示已經(jīng)播放的時間区宇,用這個屬性可以讓NowPlayingCenter顯示播放進(jìn)度;
  • MPNowPlayingInfoPropertyPlaybackRate表示播放速率值戳。通常情況下播放速率為1.0议谷,即真是時間的1秒對應(yīng)播放時間中的1秒;

這里需要解釋的是堕虹,NowPlayingCenter中的進(jìn)度刷新并不是由app不停的更新nowPlayingInfo來做的卧晓,而是根據(jù)app傳入的ElapsedPlaybackTime和PlaybackRate進(jìn)行自動刷新叶洞。例如傳入ElapsedPlaybackTime=120s,PlaybackRate=1.0禀崖,那么NowPlayingCenter會顯示2:00并且在接下來的時間中每一秒把進(jìn)度加1秒并刷新顯示衩辟。如果需要暫停進(jìn)度,傳入PlaybackRate=0.0即可波附。

所以每次播放暫停和繼續(xù)都需要更新NowPlayingCenter并正確設(shè)置ElapsedPlaybackTime和PlaybackRate否則NowPlayingCenter中的播放進(jìn)度無法正常顯示艺晴。

NowPlayingCenter的刷新時機(jī)

頻繁的刷新NowPlayingCenter并不可取,特別是在有Artwork的情況下掸屡。所以需要在合適的時候進(jìn)行刷新封寞。

依照我自己的經(jīng)驗下面幾個情況下刷新NowPlayingCenter比較合適:

  • 當(dāng)前播放歌曲進(jìn)度被拖動時
  • 當(dāng)前播放的歌曲變化時
  • 播放暫停或者恢復(fù)時
  • 當(dāng)前播放歌曲的信息發(fā)生變化時(例如Artwork仅财,duration等)

在刷新時可以適當(dāng)?shù)耐ㄟ^判斷app是否active來決定是否必須刷新以減少刷新次數(shù)狈究。

/**
 *  設(shè)置鎖屏信息
 */
-(void)configNowPlayingInfoCenter
{
    Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
    
    if (playingInfoCenter) {
        MusicInfoModel * model = [MusicPlayTools shareMusicPlay].model;
        NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld", self.index + 1]];
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithBoundsSize:image.size requestHandler:^UIImage * _Nonnull(CGSize size) {
            return image;
        }];
        //歌曲名稱
        [songInfo setObject:model.name forKey:MPMediaItemPropertyTitle];
        //演唱者
        [songInfo setObject:model.singer forKey:MPMediaItemPropertyArtist];
        //專輯名
        [songInfo setObject:@"專輯名" forKey:MPMediaItemPropertyAlbumTitle];
        //專輯縮略圖
        [songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
        //音樂當(dāng)前已經(jīng)播放時間
        NSInteger currentTime = [[MusicPlayTools shareMusicPlay] getCurTime];
        [songInfo setObject:[NSNumber numberWithInteger:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
        //進(jìn)度光標(biāo)的速度 (這個隨 自己的播放速率調(diào)整,我默認(rèn)是原速播放)
        [songInfo setObject:[NSNumber numberWithFloat:1.0] forKey:MPNowPlayingInfoPropertyPlaybackRate];
        //歌曲總時間設(shè)置
        NSInteger duration = [model.duration integerValue];
        [songInfo setObject:[NSNumber numberWithInteger:duration] forKey:MPMediaItemPropertyPlaybackDuration];
        //設(shè)置鎖屏狀態(tài)下屏幕顯示音樂信息
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盏求,一起剝皮案震驚了整個濱河市抖锥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碎罚,老刑警劉巖磅废,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異荆烈,居然都是意外死亡拯勉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門憔购,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宫峦,“玉大人,你說我怎么就攤上這事玫鸟〉急粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵鞋邑,是天一觀的道長诵次。 經(jīng)常有香客問我账蓉,道長枚碗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任铸本,我火速辦了婚禮肮雨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箱玷。我一直安慰自己怨规,他們只是感情好陌宿,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著波丰,像睡著了一般壳坪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掰烟,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天爽蝴,我揣著相機(jī)與錄音,去河邊找鬼纫骑。 笑死蝎亚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的先馆。 我是一名探鬼主播发框,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼煤墙!你這毒婦竟也來了梅惯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤仿野,失蹤者是張志新(化名)和其女友劉穎个唧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體设预,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡徙歼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳖枕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魄梯。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宾符,靈堂內(nèi)的尸體忽然破棺而出酿秸,到底是詐尸還是另有隱情,我是刑警寧澤魏烫,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布辣苏,位于F島的核電站,受9級特大地震影響哄褒,放射性物質(zhì)發(fā)生泄漏稀蟋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一呐赡、第九天 我趴在偏房一處隱蔽的房頂上張望退客。 院中可真熱鬧,春花似錦、人聲如沸萌狂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茫藏。三九已至误趴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間务傲,已是汗流浹背冤留。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留树灶,地道東北人纤怒。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像天通,于是被迫代替她去往敵國和親泊窘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容