iOS系統後臺運行機制研究

衆所周知,iOS操作系統上的App,不僅僅是外形美,同時其也是以流暢著稱,可以說是極端的流暢。在流暢的外表下,是操作系統對於程序的調度、前後臺的切換、內存的管理都達到了極致。

在日常的iOS開發中,不免會遇到需要App在被切到後臺之後,仍然需要運行一個進程完成某些特殊的操作,一般後臺運行進程作用有以下幾類:

  • 後臺更新數據、同步刷新UI
  • 與服務端通信,發送心跳,如“XMPP”類App
  • 獲取用戶地理信息數據(GPS),如新“高德地圖”
  • 多媒體App用來繼續播放音樂,如“酷狗音樂”
  • 網絡電話類App(VoIP)

要完成上述操作,有以下集中解決方案,每種方案各有優劣,適用於不同情況:


一、VoIP

優勢:

  • 永遠不會被後臺進程強殺
  • 實現起來簡單

劣勢:

  • 只能限定於VoIP類App使用。如無這VoIP功能,如果將App上傳到AppStore,則會被拒絕。(雖然看起來沒有問題,但是蘋果就是這麼“吹毛求疵”)

聽名字就知道,這個方案是Apple專門爲VoIP類App量身定製的。如果你的App並沒有VoIP相關功能,在提交AppStore時會被拒絕,但是如果你只是企業內部分發,並不提交AppStore審覈的話,就沒有啥問題了。使用方法如下:

步驟一、在XCode中爲你的Ap工程打開Background Modles,並勾選Voiceover IP選項,等於告訴iOS操作系統你需要此項功能,系統不能在後臺強制殺掉我的進程,因爲我要在後臺進行一些操作。


同時,也可以在工程的plist文件中加入如下選項:

Key:Required background modes

Value:App provides Voice over IP services


步驟二、在需要後臺運行的地方執行操作

    [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
       //幹活
    }];

這個函數會以設定的時間間隔被調用,不僅僅在後臺,App在前臺時候,這個函數一樣能夠被調用。所以可以把它當作一個橫跨前後臺的NSTimer來使用。

注意:

1、間隔時長必須大於600s,適合和服務器保持通訊,保證用戶登錄狀態。

2、復活後,執行時間只有10s,之後又繼續掛起。

3、 使用clearKeepAliveTimeout來取消喚醒

4、app必須是能和VoIP功能相關的內容,否則提交App Store會被拒絕。


二、Background fetch方式:

優勢:

  • 是官方推薦的後臺數據刷新方法
  • 實現比較簡單

劣勢:

  • 程序被喚醒執行的時間不可控
  • 被喚醒後執行時長有限制

步驟一、設置Background fetch:



步驟二、設置被喚醒最小時間間隔

[applicationsetMinimumBackgroundFetchInterval:1.0f];


步驟二、需要執行的代碼片段

 - (void)application:(UIApplication *)applicationperformFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

 

注意:

1、代碼被執行的時間間隔不可控,操作系統會以科學的方式來調用。

2、設置的時間只是最小時間間隔、操作系統只能保證在最小時間間隔內不會被調用,但是至於是1m還是1h還是多長時間調用,與操作系統當前的狀態、用戶打開App的頻率有關係。

3、被操作系統喚醒後,只能有30s的時間執行你的任務,30s後,將被再次掛起。按說以國內的網絡環境來說,基本上30s都能解決問題了。

 

三、播放空白聲音配合“backgroundTask”進行後臺運行

優勢:

  • 適用於需要進行後臺播放音樂,記錄地理位置變化等功能的App
  • 可以用來進行一些簡單的後臺操作(如保活),而不被AppStore的審覈人員拒絕,比較隱蔽
  •  喚醒時間間隔可控

劣勢:

  • 實現相對於其他幾種方案都要麻煩

步驟一:在Info.plist中,添加"Required background modes"鍵,value爲:App plays audio,申明你需要使用後臺播放音頻的功能。

 

步驟二:調節音頻會話的特性:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    NSError *setCategoryErr = nil;
    NSError *activationErr  = nil;
    [[AVAudioSession sharedInstance]
     setCategory: AVAudioSessionCategoryPlayback
     error: &setCategoryErr];
    [[AVAudioSession sharedInstance]
     setActive: YES
     error: &activationErr];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
} 

步驟三、將以下代碼添加到appDelegate文件中的- (void)applicationDidEnterBackground:(UIApplication *)application函數,也可添加到在具體類中註冊的應用進入後臺後的通知方法
- (void)applicationDidEnterBackground:(UIApplication *)application{
    UIApplication*   app = [UIApplication sharedApplication];
    __block    UIBackgroundTaskIdentifier bgTask;
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            if (bgTask != UIBackgroundTaskInvalid)
            {
                bgTask = UIBackgroundTaskInvalid;
            }
        });
    }];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            if (bgTask != UIBackgroundTaskInvalid)
            {
                bgTask = UIBackgroundTaskInvalid;
            }
        });
    });
}

這樣,就可以在你需要重複執行的地方,實現一個NSTimer,有了上面的步驟之後,你的NSTimer就能不論前臺後臺都可以運行了。


注意:

和前三個不一樣,前三個解決方案是程序被掛起之後又被喚醒,但是,這個解決方案是程序一直在後臺運行,會非常耗內存,需要開發者關注。


四、遠程消息推送,激活

優點:

  • 實現簡單
  • 喚醒時間可控,由後臺控制

缺點:

  • 需要後臺編碼,跑服務進行App喚醒

步驟一、服務端設置推送的消息中加上“content-available”:

{
     "aps" : {
          "content-available" : 1
     },
     "content-id" : 42
}


步驟二、在接收消息的地方,進行喚醒後需要的操作



五、利用NSURLSession進行background transfer task


這種解決方案詳見參考資料:http://www.starming.com/index.php?v=index&view=69
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章