iOS - 后臺保活

一、正常延長版保活(時(shí)間3min-40min不等)

1.定義定時(shí)器屬性
   @property (nonatomic,strong)NSTimer *myTimer;
   @property (nonatomic,assign)UIBackgroundTaskIdentifier backgroundTaskIdentifier;

2.代碼實(shí)現(xiàn)
- (void)applicationDidEnterBackground:(UIApplication *)application{
    self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
      [application endBackgroundTask:self.backgroundTaskIdentifier];
      self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    }];

//開啟定時(shí)器 不斷向系統(tǒng)請求后臺任務(wù)執(zhí)行的時(shí)間

     self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(makeMoreTime) userInfo:nil repeats:YES];

     [self.myTimer fire]
}

-(void)makeMoreTime {

//如果系統(tǒng)給的剩余時(shí)間小于60秒 就終止當(dāng)前的后臺任務(wù),再重新初始化一個(gè)后臺任務(wù),重新讓系統(tǒng)分配時(shí)間,這樣一直循環(huán)下去,保持APP在后臺一直處于active狀態(tài)。

MSULog(@"1 --- %.2f",[UIApplication sharedApplication].backgroundTimeRemaining);

if ([UIApplication sharedApplication].backgroundTimeRemaining < 60) {

    MSULog(@"Background Time Remaining = %.02f Seconds", [UIApplication sharedApplication].backgroundTimeRemaining);

    [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];

    self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        MSULog(@"重新");
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];

        self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

    }];
}

二、永久版?;?/h2>
第一種方式:利用后臺定位功能實(shí)現(xiàn),此模式只適合選擇“一直允許使用定位”,但是在新系統(tǒng)上會出現(xiàn)如果用戶選擇“使用app時(shí)定位”,就會出現(xiàn)退到后臺時(shí)候,手機(jī)頂部會出現(xiàn)藍(lán)色定位標(biāo)識,影響用戶體驗(yàn);如果用戶拒絕使用定位,就尷尬了,除地圖類app,其他app不建議此方式;

第二種方式:利用voip功能實(shí)現(xiàn),此模式適合音視頻類app,常規(guī)類app也可以考慮使用;voip推送和apns推送是蘋果兩大推送方式,和apns推送不一樣的地方在于,voip推送屬于強(qiáng)制推送,用戶無法拒絕此推送模式(故在iOS13后,蘋果限制voip推送使用,但不影響后臺?;睿?;實(shí)現(xiàn)voip推送,然后后臺默認(rèn)播放鈴聲!

  1. 選擇后臺模式
圖片.png
  2. 應(yīng)用AVFoundation和<PushKit/PushKit.h>框架、定義屬性
    @property (nonatomic, strong) AVAudioPlayer *player;
    @property (nonatomic,assign)UIBackgroundTaskIdentifier backgroundTaskIdentifier;

  3. 后臺鈴聲代碼實(shí)現(xiàn)
    - (AVAudioPlayer *)player{
        if (!_player){
            NSURL *url=[[NSBundle mainBundle]URLForResource:@"work5.mp3" withExtension:nil];
            _player = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
            [_player prepareToPlay];
    
            _player.volume = 0.00;
            //一直循環(huán)播放
            _player.numberOfLoops = -1;
            [[AVAudioSession sharedInstance] setActive:YES error:nil];
            [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
        }
        return _player;
    }

    - (void)startBgTask{
        UIApplication *application = [UIApplication sharedApplication];
        __block    UIBackgroundTaskIdentifier bgTask;
        bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            //這里延遲的系統(tǒng)時(shí)間結(jié)束
            [application endBackgroundTask:bgTask];
            NSLog(@"%f",application.backgroundTimeRemaining);
        }];
    }

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

     4. voip代碼實(shí)現(xiàn)(配置voip證書省略。。。。)

      -(void)startVoIPPush{
          NSString * identifier = [[NSBundle mainBundle] bundleIdentifier];

          if ([identifier isEqualToString:@"com.eccalc.chat2u"] && [[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
                //if([[UIDevice currentDevice].systemVersion floatValue] < 10)
          {
                PKPushRegistry * pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
                pushRegistry.delegate = self;
                pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
          //}else{
  //            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  //            center.delegate = self;
  //            [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
  //                                  completionHandler:^(BOOL granted, NSError * _Nullable error) {
  //                                      // Enable or disable features based on authorization.
  //                                  }];
  //            [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
  //            }];
          }
    
  //        UIUserNotificationType types = (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
 //        UIUserNotificationSettings * notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
//        [[UIApplication sharedApplication]registerUserNotificationSettings:notificationSettings];
      }else{
          g_default removeObjectForKey:@"voipToken"];
      }
  }

  #pragma mark - PKPushRegistryDelegate
  -(void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type{
      if ([credentials.token length] == 0) {
  //        MSULog(@"voip token NULL");
          return;
      }
      NSString * voipToken;

      if (@available(iOS 13.0, *)) {
          voipToken = [self getHexStringForData:credentials.token];
      }else {    
          voipToken = [[[[credentials.token description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];
      }
      //voipToken 需要提交給服務(wù)器
      [g_default setObject:voipToken forKey:@"voipToken"];
  //    MSULog(@"voipToken:%@",voipToken);
  }

  #pragma mark - 但在iOS11.0之后,系統(tǒng)也提供了另外一個(gè)方法實(shí)現(xiàn)喚醒后的操作,此方法開啟后,我項(xiàng)目iOS13以下機(jī)型會出現(xiàn)受收不到voip消息情況,所以注釋了
  //- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion
  //{
  //    if (type != PKPushTypeVoIP) {
  //        return;
  //    }
  //    [self dealWithPushkitDidReceiveIncomingPushWithPayload:payload];
  //}
  #pragma mark - iOS8.0~iOS11.0是使用上面這個(gè)代理方法來實(shí)現(xiàn)喚醒后的操作,
  -(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type{
      if (type != PKPushTypeVoIP) {
          return;
      }
      //此處寫相關(guān)處理邏輯
      //[self dealWithPushkitDidReceiveIncomingPushWithPayload:payload];   
  }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容