現(xiàn)在后臺只有兩種方式一直運(yùn)行。其他都不可行
成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供涿鹿網(wǎng)站建設(shè)、涿鹿做網(wǎng)站、涿鹿網(wǎng)站設(shè)計、涿鹿網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、涿鹿企業(yè)網(wǎng)站模板建站服務(wù),10年涿鹿做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
1、音樂允許后臺一直運(yùn)行。
2、定位允許后臺一直運(yùn)行。
由于蘋果的這種特性,建議在前臺時掃描藍(lán)牙設(shè)備時,設(shè)置CBCentralManagerScanOptionAllowDuplicatesKey為NO;在后臺掃描藍(lán)牙時,設(shè)置CBCentralManagerScanOptionAllowDuplicatesKey為YES
iOS 4開始引入的multitask,我們可以實現(xiàn)像ipod程序那樣在后臺播放音頻了。如果音頻操作是用蘋果官方的AVFoundation.framework實現(xiàn),像用AvAudioPlayer,AvPlayer播放的話,要實現(xiàn)完美的后臺音頻播放,依據(jù)app的功能需要,可能需要實現(xiàn)幾個關(guān)鍵的功能。
首先,播放音頻之前先要設(shè)置AVAudioSession模式,通常只用來播放的App可以設(shè)為AVAudioSessionCategoryPlayback即可。模式意義及其他模式請參考文檔。
1 //后臺播放音頻設(shè)置
2 AVAudioSession *session = [AVAudioSession sharedInstance];
3 [session setActive:YES error:nil];
4 [session setCategory:AVAudioSessionCategoryPlayback error:nil];
1.通知IOS該app支持background audio。缺省情況下,當(dāng)按下home鍵時,當(dāng)前正在運(yùn)行的程序被suspend,狀態(tài)從active變成in-active,也就是說如果正在播放音頻,按下HOME后就會停止。這里需要讓app在按在HOME后,轉(zhuǎn)到后臺運(yùn)行而非被suspend,解決辦法是在程序的-info.plist中增加required background modes這個key項,并選擇App plays audio or streams audio/video using AirPlay這個value項(如果用過Xcode5.0,在TARGETS-Capabilities-Background Modes設(shè)置為ON,勾選Audio and AirPlay選項)。
2.如果你在后臺播放使用的時加載網(wǎng)絡(luò)音頻,恰巧網(wǎng)速很慢,音頻被停止下來這時候程序也隨之suspend,曾經(jīng)有山寨的解決辦法是專門起一個player的實例連續(xù)不停的放同一無聲音片斷,阻止程序被suspend。這里提供的方法是通過申請后臺taskID達(dá)到后臺切換播放文件的功能。
即使聲明taskID也最多只能在后臺運(yùn)行600秒鐘。(在ios7sdk中可以使用NSURLSession來實現(xiàn)后臺緩沖)
(一般情況下,按HOME將程序送到后臺,可以有5或10秒時間可以進(jìn)行一些收尾工作,具體時間[[UIApplication sharedApplication] backgroundTimeRemaining]返回值,超時后app會被suspend。)
3.ipod播放程序在后臺時,雙擊HOME鍵,會有個控制界面,可以對它進(jìn)行播放控制(暫停開始、上一曲、下一曲)。如果您想讓您的app可以像ipod一樣在后臺也可以方便的通過雙擊HOME鍵來控制(在ios7中是使用上拉菜單控制),就要用到遠(yuǎn)程控制事件了。
首先在viewdidload等初始化的地方聲明App接收遠(yuǎn)程控制事件,并在相應(yīng)地方結(jié)束聲明
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
當(dāng)然也不一定是在viewcontroller中,也可以是在applicationDidEnterBackground:方法中開始接受遠(yuǎn)程控制,applicationDidBecomeActive:中結(jié)束接受遠(yuǎn)程控制,但是當(dāng)前的appdelegate中要繼承與UIResponder,因為在激活遠(yuǎn)程控制以后要把當(dāng)前類變成第一響應(yīng),重寫canBecomeFirstResponder方法。
最后定義?remoteControlReceivedWithEvent,處理具體的播放、暫停、前進(jìn)、后退等具體事件
//重寫父類方法,接受外部事件的處理
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[self playAndStopSong:self.playButton];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[self playLastButton:self.lastButton];
break;
case UIEventSubtypeRemoteControlNextTrack:
[self playNextSong:self.nextButton];
break;
case UIEventSubtypeRemoteControlPlay:
[self playAndStopSong:self.playButton];
break;
case UIEventSubtypeRemoteControlPause:
[self playAndStopSong:self.playButton];
break;
default:
break;
}
}
}
其它外部事件也可通過這種方式實現(xiàn),如“搖一搖”響應(yīng)等。
4. 至此,您有播放App已經(jīng)基本完成了,其次插拔耳機(jī)是否響應(yīng)停止播放時間需要進(jìn)一步研究耳機(jī)檢測和聲音路由切換的問題,再次不詳細(xì)講述。
5. 還有一些開發(fā)者可能會發(fā)現(xiàn),有一些音視頻app在定義的時候自定一些控件可以調(diào)節(jié)系統(tǒng)的音量大小,不需要用戶調(diào)整音量按鈕。經(jīng)查看相關(guān)的資料總結(jié)出有兩種方法:
一種是調(diào)用控件MPVolumeView在屏幕中創(chuàng)建一個音量條,拖動可以改變系統(tǒng)的音量大小。
另一種是使用MPMusicPlayerController類,可以自定義控件調(diào)整系統(tǒng)音量的大?。ǖ窃趇os7sdk中已經(jīng)被棄用,估計以后幾個版本中可能找不到這個方法了)。
MPMusicPlayerController *mpc = [MPMusicPlayerController applicationMusicPlayer];
mpc.volume = 0;? //0.0~1.0
6. 在一些其他的音樂播放軟件中如:酷我、qq音樂等,你會發(fā)在播放的時候,當(dāng)設(shè)備鎖屏以后依然可以看到用戶播放的音樂名稱、演唱者、專輯名稱、音樂時長、專輯圖片等信息。這些就需要在用戶切換完歌去的時候,在程序中設(shè)置信息了。
//設(shè)置鎖屏狀態(tài),顯示的歌曲信息
-(void)configNowPlayingInfoCenter{
if (NSClassFromString(@"MPNowPlayingInfoCenter")) {
NSDictionary *info = [self.musicList objectAtIndex:_playIndex];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
//歌曲名稱
[dict setObject:[info objectForKey:@"name"] forKey:MPMediaItemPropertyTitle];
//演唱者
[dict setObject:[info objectForKey:@"singer"] forKey:MPMediaItemPropertyArtist];
//專輯名
[dict setObject:[info objectForKey:@"album"] forKey:MPMediaItemPropertyAlbumTitle];
//專輯縮略圖
UIImage *image = [UIImage imageNamed:[info objectForKey:@"image"]];
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
[dict setObject:artwork forKey:MPMediaItemPropertyArtwork];
//音樂剩余時長
[dict setObject:[NSNumber numberWithDouble:self.player.duration] forKey:MPMediaItemPropertyPlaybackDuration];
//音樂當(dāng)前播放時間 在計時器中修改
//[dict setObject:[NSNumber numberWithDouble:0.0] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
//設(shè)置鎖屏狀態(tài)下屏幕顯示播放音樂信息
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
}
}
上面的if?(NSClassFromString(@”MPNowPlayingInfoCenter”))語句,說是為了避免了版本兼容問題,這個API貌似只出現(xiàn)在5里面。
7. 下面就在計時器中不斷刷新鎖屏狀態(tài)下的播放進(jìn)度條了。
//計時器修改進(jìn)度
- (void)changeProgress:(NSTimer *)sender{
if(self.player){
//當(dāng)前播放時間
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo]];
[dict setObject:[NSNumber numberWithDouble:self.player.currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; //音樂當(dāng)前已經(jīng)過時間
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
}
}
8. 當(dāng)前的很多常見的播放器都可以在鎖屏狀態(tài)下顯示顯示歌詞,經(jīng)過一番查找后,終于找到方法(詳情: 點(diǎn)擊查看 ),大致就是根據(jù)播放的時間和歌詞顯示時間,利用計時器不斷的用歌詞和專輯封面合成圖片,達(dá)到顯示歌詞的效果。還有就是在屏幕變暗停止這一操作、屏幕點(diǎn)亮的時候開始計時器,以節(jié)省電量和cpu,有兩種方法可以監(jiān)聽上述現(xiàn)象:
一種是監(jiān)聽內(nèi)核層DarwinNotification,在Darwin中,有很多的系統(tǒng)事件,但apple的api文檔描述這些api使用有限制,也就是灰色地帶的api,所以能不用則不用;
另一種方法可以通過notify_get_state來獲取com.apple.springboard.hasBlankedScreen?的狀態(tài)值,通過狀態(tài)值我們可以判斷屏幕狀態(tài),屏幕亮或者暗系統(tǒng)會給出不同狀態(tài)值,然后根據(jù)狀態(tài)值,通過NotificationCenter發(fā)送消息通知給相應(yīng)的函數(shù)處理。
1、首先,打開蘋果手機(jī)上面的“設(shè)置”,進(jìn)入手機(jī)的系統(tǒng)功能設(shè)置,如圖所示。
2、進(jìn)入設(shè)置功能之后,選擇其中的“通用”,進(jìn)入通用功能設(shè)置,如圖所示。
3、進(jìn)入蘋果手機(jī)的通用功能設(shè)置之后,選擇其中的“后臺應(yīng)用刷新”,并點(diǎn)擊進(jìn)入,如圖所示。
4、進(jìn)入之后,可以看到“后臺應(yīng)用刷新”功能默認(rèn)是打開的狀態(tài),點(diǎn)擊進(jìn)入即可,如圖所示。
5、如果是打開狀態(tài),當(dāng)使用無線局域網(wǎng)或蜂窩移動網(wǎng)時,允許應(yīng)用在后臺刷新內(nèi)容。
6、可以看到當(dāng)前設(shè)置為在使用“WLAN和蜂窩移動網(wǎng)”時,是允許應(yīng)用后臺刷新的,這里選擇關(guān)閉,就可以關(guān)閉所有后臺運(yùn)行的程序,如圖所示。
7、也可以在后臺應(yīng)用刷新設(shè)置頁面,手動關(guān)閉所有應(yīng)用后面的按鈕,使所有應(yīng)用都不能在后臺刷新,同樣可以達(dá)到關(guān)閉所有后臺運(yùn)行程序的目的,如圖所示。
最近在做番茄鐘的功能。首先簡單介紹一下番茄鐘吧,就是25分鐘工作番茄工作法。先說一下** 番茄工作法 **:
那么功能就相當(dāng)于一個25分鐘的鬧鐘,可以播放背景音樂,到點(diǎn)給用戶提醒。
功能聽起來很簡單是不是?其實挺多坑的。
開發(fā)過程中遇到了2個問題。
OK,下面我們一步一步來分析并解決這兩個問題。
** 首先要理解iOS系統(tǒng)的后臺機(jī)制 **
我們都知道,蘋果對APP占用硬件資源管的很嚴(yán),更不要說應(yīng)用后臺時候的資源占用了。正常情況下,使用應(yīng)用時,APP從硬盤加載到內(nèi)存,開始工作;當(dāng)用戶按下home鍵,APP便被掛起,依然駐留在內(nèi)存中,這種狀態(tài)下,不調(diào)用蘋果已開放的幾種后臺方法,程序便不會運(yùn)行;如果在這個時候,使程序繼續(xù)運(yùn)行,則為后臺狀態(tài);如果當(dāng)前內(nèi)存將要不夠用時,系統(tǒng)會自動把之前掛起狀態(tài)下的APP請出內(nèi)存。所以我們看到,有些時候打開APP時,還是上次退出時的那個頁面那些數(shù)據(jù),有時則是重新從閃屏進(jìn)入。
iOS系統(tǒng)后臺機(jī)制大概可以分為5種狀態(tài)
** 那么我需要的是Background模式。即APP在后臺運(yùn)行同時保持程序active的狀態(tài) **
首先去xCode里面設(shè)置。到info.plist中添加以下信息:
然后到Capabilities里面打開后臺模式,并根據(jù)項目的要求勾選對應(yīng)的功能。我這里只需要保持后臺運(yùn)行并且播放背景音樂及通知功能。所以就勾選了第一個和最后一個
以上這兩步是告訴系統(tǒng)我這個APP支持后臺模式,對應(yīng)的環(huán)境為音頻環(huán)境。
可是到這一步,APP還是不能長時間運(yùn)行到后臺。
為什么?我們思考一下。我們讓程序支持了后臺運(yùn)行的模式。那么我們是不是還需要系統(tǒng)知道我們的程序要在后臺運(yùn)行多久呢?我們需要告訴系統(tǒng)我們期望APP在后臺存活的時間。
首先聲明一個屬性
在進(jìn)入后臺的時候通過AppDelegate里面的方法:
現(xiàn)在就可以讓我們的APP一直運(yùn)行在后臺啦!總結(jié)下來的思路就是:通過一個后臺任務(wù)(這個任務(wù)我們也不用管,它存在的意義就是和系統(tǒng)去請求后臺運(yùn)行的一定的時間),這個時間我們不知道也不用去管,我們可以通過該時間還剩下多少判斷是否繼續(xù)請求時間,如此循環(huán),我們就可以不斷的請求時間來保持我們的app一直運(yùn)行在后臺。
接下來解決音樂在后臺模式(切換到后臺或者鎖屏狀態(tài))下停止播放的問題。
其實很簡單。
下面解釋一下AVAudioSession的一些設(shè)置參數(shù)
Demo地址:
** 歡迎star!**
iOS應(yīng)用的運(yùn)行狀態(tài)分為以下四種:
1、Not running:應(yīng)用還沒有啟動,或者應(yīng)用正在運(yùn)行但是途中被系統(tǒng)停止。
2、Inactive:當(dāng)前應(yīng)用正在前臺運(yùn)行,但是并不接收事件(當(dāng)前或許正在執(zhí)行其它代碼)。一般每當(dāng)應(yīng)用要從一個狀態(tài)切換到另一個不同的狀態(tài)時,中途過渡會短暫停留在此狀態(tài)。
3、Active:當(dāng)前應(yīng)用正在前臺運(yùn)行,并且接收事件。這是應(yīng)用正在前臺運(yùn)行時所處的正常狀態(tài)。
4、Suspended:應(yīng)用處在后臺,并且已停止執(zhí)行代碼。系統(tǒng)自動的將應(yīng)用移入此狀態(tài),且在此舉之前不會對應(yīng)用做任何通知。當(dāng)處在此狀態(tài)時,應(yīng)用依然駐留內(nèi)存但不執(zhí)行任何程序代碼。當(dāng)系統(tǒng)發(fā)生低內(nèi)存告警時,系統(tǒng)將會將處于 Suspended 狀態(tài)的應(yīng)用清除出內(nèi)存以為正在前臺運(yùn)行的應(yīng)用提供足夠的內(nèi)存。