最近在做番茄鐘的功能。首先簡單介紹一下番茄鐘吧,就是25分鐘工作番茄工作法。先說一下** 番茄工作法 **:
番茄工作法是簡單易行的時間管理方法,是由弗朗西斯科·西里洛于1992年創(chuàng)立的一種相對于GTD更微觀的時間管理方法。
使用番茄工作法,選擇一個待完成的任務(wù),將番茄時間設(shè)為25分鐘,專注工作,中途不允許做任何與該任務(wù)無關(guān)的事,直到番茄時鐘響起,然后在紙上畫一個X短暫休息一下(5分鐘就行),每4個番茄時段多休息一會兒。
番茄工作法極大地提高了工作的效率,還會有意想不到的成就感。
那么功能就相當(dāng)于一個25分鐘的鬧鐘,可以播放背景音樂,到點給用戶提醒。
功能聽起來很簡單是不是?其實挺多坑的。
開發(fā)過程中遇到了2個問題。
- 因為番茄鐘是25分鐘,那么當(dāng)用戶開啟番茄鐘后很可能在中途就將APP切換到了后臺,那么幾分鐘程序就會被系統(tǒng)kill掉。
- 當(dāng)用戶開啟番茄鐘的背景音樂時,APP切換到后臺或者鎖屏狀態(tài)時,音樂都會立即停止播放。
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)用蘋果已開放的幾種后臺方法,程序便不會運行;如果在這個時候,使程序繼續(xù)運行,則為后臺狀態(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)
- Not Running:APP沒有啟動,也沒有后臺運行。
- Active:用戶正在使用APP,比如說我們聊微信看網(wǎng)頁的時候,APP就處于Active狀態(tài)。
- Inactive:這是一個過渡的狀態(tài),APP雖然打開了,但是用戶沒有跟APP有任何互動操作。
- Background:APP在后臺運行,微信會在沒有打開的時候接收消息。
- Suspended:APP雖然在后臺運行,但是處于休眠狀態(tài),只占用一點內(nèi)存。
** 那么我需要的是Background模式。即APP在后臺運行同時保持程序active的狀態(tài) **
首先去xCode里面設(shè)置。到info.plist中添加以下信息:
然后到Capabilities里面打開后臺模式,并根據(jù)項目的要求勾選對應(yīng)的功能。我這里只需要保持后臺運行并且播放背景音樂及通知功能。所以就勾選了第一個和最后一個
以上這兩步是告訴系統(tǒng)我這個APP支持后臺模式,對應(yīng)的環(huán)境為音頻環(huán)境。
可是到這一步,APP還是不能長時間運行到后臺。
為什么?我們思考一下。我們讓程序支持了后臺運行的模式。那么我們是不是還需要系統(tǒng)知道我們的程序要在后臺運行多久呢?我們需要告訴系統(tǒng)我們期望APP在后臺存活的時間。
首先聲明一個屬性
@property (nonatomic, assign) UIBackgroundTaskIdentifier bgTask;
在進(jìn)入后臺的時候通過AppDelegate里面的方法:
-(void)applicationDidEnterBackground:(UIApplication *)application{
[ self comeToBackgroundMode];
}
-(void)comeToBackgroundMode{
//初始化一個后臺任務(wù)BackgroundTask,這個后臺任務(wù)的作用就是告訴系統(tǒng)當(dāng)前app在后臺有任務(wù)處理,需要時間
UIApplication* app = [UIApplication sharedApplication];
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
//開啟定時器 不斷向系統(tǒng)請求后臺任務(wù)執(zhí)行的時間
self.timer = [NSTimer scheduledTimerWithTimeInterval:25.0 target:self selector:@selector(applyForMoreTime) userInfo:nil repeats:YES];
[self.timer fire];
}
-(void)applyForMoreTime {
//如果系統(tǒng)給的剩余時間小于60秒 就終止當(dāng)前的后臺任務(wù),再重新初始化一個后臺任務(wù),重新讓系統(tǒng)分配時間,這樣一直循環(huán)下去,保持APP在后臺一直處于active狀態(tài)。
if ([UIApplication sharedApplication].backgroundTimeRemaining < 60) {
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
}
}
現(xiàn)在就可以讓我們的APP一直運行在后臺啦!總結(jié)下來的思路就是:通過一個后臺任務(wù)(這個任務(wù)我們也不用管,它存在的意義就是和系統(tǒng)去請求后臺運行的一定的時間),這個時間我們不知道也不用去管,我們可以通過該時間還剩下多少判斷是否繼續(xù)請求時間,如此循環(huán),我們就可以不斷的請求時間來保持我們的app一直運行在后臺。
接下來解決音樂在后臺模式(切換到后臺或者鎖屏狀態(tài))下停止播放的問題。
其實很簡單。
//設(shè)置后臺模式和鎖屏模式下依然能夠播放
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
//初始化播放器和兩個音頻(一個有聲 一個無聲)
NSURL *urlSound = [[NSURL alloc]initWithString:[[NSBundle mainBundle]pathForResource:@"pomodoSound" ofType:@"m4a"]];
playerSound = [[AVAudioPlayer alloc] initWithContentsOfURL:urlSound error:&playerError];
NSURL *urlNoSound = [[NSURL alloc]initWithString:[[NSBundle mainBundle]pathForResource:@"backSound" ofType:@"mp3"]];
playerNoSound = [[AVAudioPlayer alloc] initWithContentsOfURL:urlNoSound error:&playerError];
playerSound.numberOfLoops = -1;
playerNoSound.numberOfLoops = -1;
player = playerSound;
[player play];
下面解釋一下AVAudioSession的一些設(shè)置參數(shù)
- NSString *const AVAudioSessionCategoryAmbient;
靜音模式或者鎖屏下不再播放音樂,和其他app聲音混合。 - NSString *const AVAudioSessionCategorySoloAmbient;
默認(rèn)模式,靜音模式或者鎖屏下不再播放音樂,不和其他app聲音混合。 - NSString *const AVAudioSessionCategoryPlayback;
表示對于用戶切換靜音模式或者鎖屏 都不理睬,繼續(xù)播放音樂。并且不播放來自其他app的音樂 - NSString *const AVAudioSessionCategoryRecord;
不播放音樂,鎖屏狀態(tài)繼續(xù)錄音 - NSString *const AVAudioSessionCategoryPlayAndRecord;
播放音樂,并錄音
Demo地址:https://github.com/BoYangZuo/KeepAppActive
** 歡迎star!**