iOS"偽后臺"機制下如何保持APP一直運行在后臺

最近在做番茄鐘的功能。首先簡單介紹一下番茄鐘吧,就是25分鐘工作番茄工作法。先說一下** 番茄工作法 **:

番茄工作法是簡單易行的時間管理方法,是由弗朗西斯科·西里洛于1992年創立的一種相對于GTD更微觀的時間管理方法。
使用番茄工作法,選擇一個待完成的任務,將番茄時間設為25分鐘,專注工作,中途不允許做任何與該任務無關的事,直到番茄時鐘響起,然后在紙上畫一個X短暫休息一下(5分鐘就行),每4個番茄時段多休息一會兒。
番茄工作法極大地提高了工作的效率,還會有意想不到的成就感。

那么功能就相當于一個25分鐘的鬧鐘,可以播放背景音樂,到點給用戶提醒。

功能聽起來很簡單是不是?其實挺多坑的。

開發過程中遇到了2個問題。

  1. 因為番茄鐘是25分鐘,那么當用戶開啟番茄鐘后很可能在中途就將APP切換到了后臺,那么幾分鐘程序就會被系統kill掉。
  2. 當用戶開啟番茄鐘的背景音樂時,APP切換到后臺或者鎖屏狀態時,音樂都會立即停止播放。

OK,下面我們一步一步來分析并解決這兩個問題。

** 首先要理解iOS系統的后臺機制 **

我們都知道,蘋果對APP占用硬件資源管的很嚴,更不要說應用后臺時候的資源占用了。正常情況下,使用應用時,APP從硬盤加載到內存,開始工作;當用戶按下home鍵,APP便被掛起,依然駐留在內存中,這種狀態下,不調用蘋果已開放的幾種后臺方法,程序便不會運行;如果在這個時候,使程序繼續運行,則為后臺狀態;如果當前內存將要不夠用時,系統會自動把之前掛起狀態下的APP請出內存。所以我們看到,有些時候打開APP時,還是上次退出時的那個頁面那些數據,有時則是重新從閃屏進入。

iOS系統后臺機制大概可以分為5種狀態

  • Not Running:APP沒有啟動,也沒有后臺運行。
  • Active:用戶正在使用APP,比如說我們聊微信看網頁的時候,APP就處于Active狀態。
  • Inactive:這是一個過渡的狀態,APP雖然打開了,但是用戶沒有跟APP有任何互動操作。
  • Background:APP在后臺運行,微信會在沒有打開的時候接收消息。
  • Suspended:APP雖然在后臺運行,但是處于休眠狀態,只占用一點內存。

** 那么我需要的是Background模式。即APP在后臺運行同時保持程序active的狀態 **

首先去xCode里面設置。到info.plist中添加以下信息:

Snip20170301_13.png

然后到Capabilities里面打開后臺模式,并根據項目的要求勾選對應的功能。我這里只需要保持后臺運行并且播放背景音樂及通知功能。所以就勾選了第一個和最后一個

Snip20170301_14.png

以上這兩步是告訴系統我這個APP支持后臺模式,對應的環境為音頻環境。

可是到這一步,APP還是不能長時間運行到后臺。

為什么?我們思考一下。我們讓程序支持了后臺運行的模式。那么我們是不是還需要系統知道我們的程序要在后臺運行多久呢?我們需要告訴系統我們期望APP在后臺存活的時間。

首先聲明一個屬性

 @property (nonatomic, assign) UIBackgroundTaskIdentifier bgTask;

在進入后臺的時候通過AppDelegate里面的方法:

-(void)applicationDidEnterBackground:(UIApplication *)application{
  [ self comeToBackgroundMode];
}

-(void)comeToBackgroundMode{
    //初始化一個后臺任務BackgroundTask,這個后臺任務的作用就是告訴系統當前app在后臺有任務處理,需要時間
    UIApplication*  app = [UIApplication sharedApplication];
    self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:self.bgTask];
    self.bgTask = UIBackgroundTaskInvalid;
    }];
    //開啟定時器 不斷向系統請求后臺任務執行的時間
    self.timer = [NSTimer scheduledTimerWithTimeInterval:25.0 target:self selector:@selector(applyForMoreTime) userInfo:nil repeats:YES];
    [self.timer fire];
}

-(void)applyForMoreTime {
   //如果系統給的剩余時間小于60秒 就終止當前的后臺任務,再重新初始化一個后臺任務,重新讓系統分配時間,這樣一直循環下去,保持APP在后臺一直處于active狀態。
    if ([UIApplication sharedApplication].backgroundTimeRemaining < 60) {
    [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];
    }
}

現在就可以讓我們的APP一直運行在后臺啦!總結下來的思路就是:通過一個后臺任務(這個任務我們也不用管,它存在的意義就是和系統去請求后臺運行的一定的時間),這個時間我們不知道也不用去管,我們可以通過該時間還剩下多少判斷是否繼續請求時間,如此循環,我們就可以不斷的請求時間來保持我們的app一直運行在后臺。

接下來解決音樂在后臺模式(切換到后臺或者鎖屏狀態)下停止播放的問題。

其實很簡單。

//設置后臺模式和鎖屏模式下依然能夠播放
[[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的一些設置參數

  • NSString *const AVAudioSessionCategoryAmbient;
    靜音模式或者鎖屏下不再播放音樂,和其他app聲音混合。
  • NSString *const AVAudioSessionCategorySoloAmbient;
    默認模式,靜音模式或者鎖屏下不再播放音樂,不和其他app聲音混合。
  • NSString *const AVAudioSessionCategoryPlayback;
    表示對于用戶切換靜音模式或者鎖屏 都不理睬,繼續播放音樂。并且不播放來自其他app的音樂
  • NSString *const AVAudioSessionCategoryRecord;
    不播放音樂,鎖屏狀態繼續錄音
  • NSString *const AVAudioSessionCategoryPlayAndRecord;
    播放音樂,并錄音

Demo地址:https://github.com/BoYangZuo/KeepAppActive

** 歡迎star!**

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容