iOS開發基礎102-後臺保活方案

Mr.陳發表於2024-07-16

iOS系統在後臺執行程式時,有嚴格的限制,為了更好地管理資源和電池壽命,iOS會限制應用程式在後臺的執行時間。然而,iOS提供了一些特定的策略和技術,使得應用程式可以在特定場景下保持後臺執行(即“後臺保活”)。以下是iOS中幾種常見的後臺保活方案,並附上示例程式碼:

一、後臺任務

利用beginBackgroundTaskendBackgroundTask來執行後臺任務。後臺任務將在應用程式進入後臺時仍能保持有限的時間執行任務。

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (assign, nonatomic) UIBackgroundTaskIdentifier bgTask;

@end

@implementation AppDelegate

- (void)applicationDidEnterBackground:(UIApplication *)application {
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 在這裡執行你的後臺任務
        for (int i = 0; i < 100; i++) {
            NSLog(@"Background task running %d", i);
            [NSThread sleepForTimeInterval:1];
        }
        
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    });
}

@end

二、使用Background Fetch

利用Background Fetch,系統會間歇性地喚醒應用程式,以便它可以執行任務或獲取資料。需要在Xcode的“Capabilities”中開啟Background Modes,並勾選“Background fetch”。

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
    return YES;
}

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // 在這裡執行你的後臺資料獲取任務
    NSLog(@"Background fetch started");

    // 模擬資料獲取
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"Background fetch completed");
        completionHandler(UIBackgroundFetchResultNewData);
    });
}

@end

三、使用遠端通知(Silent Push Notification)

利用遠端通知,在接收到通知時,系統會喚醒應用程式執行指定的任務。需要開啟Remote notifications,在Application Capabilities中勾選“Remote notifications”。

#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [UNUserNotificationCenter currentNotificationCenter].delegate = self;
    [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            [[UIApplication sharedApplication] registerForRemoteNotifications];
        }
    }];
    return YES;
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // 在這裡處理收到的遠端通知
    NSLog(@"Received remote notification");

    // 模擬處理任務
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"Handled remote notification");
        completionHandler(UIBackgroundFetchResultNewData);
    });
}

@end

四、使用特定的後臺模式(Background Modes)

iOS提供了一些特定的後臺模式,允許程式在後臺持續執行。常見的後臺模式包括:

  • Audio: 允許應用程式在後臺播放音訊。
  • Location: 允許應用程式在後臺持續獲取位置更新。
  • VoIP: 允許應用程式在後臺偵聽VoIP事件。
  • Bluetooth: 允許應用程式與藍芽裝置通訊。

1. Audio後臺模式

需要在Xcode的“Capabilities”中開啟Background Modes,並勾選“Audio, AirPlay, and Picture in Picture”。

#import <AVFoundation/AVFoundation.h>

@interface AppDelegate ()

@property (nonatomic, strong) AVAudioPlayer *audioPlayer;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSError *error = nil;
    NSURL *audioURL = [[NSBundle mainBundle] URLForResource:@"audioFileName" withExtension:@"mp3"];
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:&error];
    [self.audioPlayer prepareToPlay];
    
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
    [audioSession setActive:YES error:&error];
    
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    [self.audioPlayer play];
}

@end

2. Location後臺模式

需要在Xcode的“Capabilities”中開啟Background Modes,並勾選“Location updates”。

#import <CoreLocation/CoreLocation.h>

@interface AppDelegate () <CLLocationManagerDelegate>

@property (nonatomic, strong) CLLocationManager *locationManager;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    [self.locationManager requestAlwaysAuthorization];
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    [self.locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    CLLocation *location = [locations lastObject];
    NSLog(@"Background location: %@", location);
}

@end

五、使用後臺URLSession

使用NSURLSession來執行後臺下載和上傳任務。需要在後臺配置中開啟Background Modes,並勾選“Background fetch”和“Remote notifications”。

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, NSURLSessionDelegate, NSURLSessionDownloadDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong) NSURLSession *backgroundSession;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.example.background"];
    self.backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSURL *url = [NSURL URLWithString:@"http://example.com/largefile.zip"];
    NSURLSessionDownloadTask *downloadTask = [self.backgroundSession downloadTaskWithURL:url];
    [downloadTask resume];
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    NSLog(@"Download completed: %@", location);
    // 處理下載結果,比如儲存檔案
}

@end

透過上述幾種方案,我們可以在iOS應用程式中實現各種場景下的後臺保活。每種方案都有其適用的場景和限制,開發者需要根據應用的實際需求和系統提供的特性,選擇合適的後臺保活方案。

相關文章