2025-05-23

今天要實現一個需求,當用戶觸摸HOME鍵,將應用切換到後臺時,啟動自動備份的任務。這涉及到ios的後臺任務處理,本文簡單總結一下

首先,ios app有5種狀態,分別是:not running, inactive, active, background, suspended,詳情請看官方的guide:

apple guide

機制

如果應用處於background狀態,又希望它繼續做一些事的話,ios提供瞭幾種途徑:

推送

蘋果提供的PUSH機制,叫APNS。騰訊的QQ和微信就是使用這種方式。實際上,使用長連接會更好,但是蘋果不支持。我理解其實應用已經suspended,但是當接收到push的數據以後,會短暫地回到background進行處理,處理完畢以後又回到suspended狀態

從ios7開始,分為local push和remote push,我們應用現在還沒用到,暫不深究

智能調度

太玄乎瞭,不太瞭解

特定的多任務

某些特定的任務可以在後臺長時間運行,比如VOIP,location service等,隻有特定類型的任務,才能用這種方式,適用性不強

後臺上傳下載

類似於特定多任務,隻有特殊的任務才能用

task completion

這種方式的適用性比較強,我最後也是采取這種方式來實現的。因此本文重點介紹這種

通常情況下,應用在進入background之後,很快就會轉到suspended狀態。但是,如果應用有需要的話(比如我們這個需求),可以向系統申請一點額外的時間來完成當前的任務

代碼:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    __block UIBackgroundTaskIdentifier bgTask;// 後臺任務標識
    
    // 結束後臺任務
    void (^endBackgroundTask)() = ^(){
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    };
    
    bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        endBackgroundTask();
    }];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        double start_time = application.backgroundTimeRemaining;// 記錄後臺任務開始時間
        
        BOOL networkAvailable = [YLSGlobalUtils isNetworkAvailable];
        if(!networkAvailable){
            NSLog(@"網絡不可用,取消自動備份");
            endBackgroundTask();
            return;
        }
        
        BOOL need = [backupService checkNeedBackup];
        if(!need){
            NSLog(@"無需備份");
            endBackgroundTask();
            return;
        }
        
        [backupService doBackupProcessHandler:^(float done, float total){
            // nothing to do with progress
        } CompletionHandler:^(NSError* error, NSArray* statistics){
            double done_time = application.backgroundTimeRemaining;
            double spent_time = start_time - done_time;
            NSLog(@"後臺備份完成,耗時: %f秒", spent_time);
            endBackgroundTask();
        }];
    });
}

核心是這個方法:

- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void(^)(void))handler

註冊一個後臺任務,這個任務最多隻有10分鐘時間,如果超時,則會調用參數中的block,在此block中,必須調用這個方法:

- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier

否則,應用會crash。隻要調用瞭beginBackgroundTaskWithExpirationHandler:方法,就必須在handler裡相應地調用endBackgroundTask:方法

實際的邏輯,在下面的block裡完成,這裡沒什麼特別的,隻是如果提前結束瞭任務,也調用一次endBackgroundTask:方法,這樣就不會超時,前面的expirationHandler就不會被執行

另外,通過UIApplication的backgroundTimeRemaining屬性,可以獲取此後臺任務還剩餘的時間(當此值變成0,expirationHandler就被執行)

使用時機

這段代碼,是寫在ApplicationDelegate的生命周期方法裡:

- (void)applicationDidEnterBackground:(UIApplication *)application

這主要是因為,我們就是希望僅當應用被切換到後臺時才開始自動備份。但是後臺任務並不是隻能在這種情況下啟動,如果應用中有一些關鍵性的任務,希望即使被切換到後臺也要先完成再suspend,就可以隨時調用

- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void(^)(void))handler

以確保此任務完成,寫法隻要參考上面的代碼就行瞭

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *