zoukankan      html  css  js  c++  java
  • IOS 后台挂起程序 当程序到后台后,继续完成LongRunning Task 任务

    我们知道,到我们程序从前台退到后台(安home)键后,将执行程序的委托方法。

    // 当应用程序掉到后台时,执行该方法

    - (void)applicationDidEnterBackground:(UIApplication *)application

    {

       

    }

    我们已经知道:

    当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。

    我们假设有这么一种情况:
    当我们的应用程序从前台被送到了后台。
    这时候,我们的程序将执行委托方法 applicationDidEnterBackground。但是,这时候,应用程序只给了我们可怜的一点点时间(也就是秒级别的)来处理东西,然后,所有的线程都被挂起了。
    而实际中,我们可能需要更长的时间来完成我们的需要的必要操作:

    1.我们需要在应用程序推到后台时,能够有足够的时间来完成将数据保存到远程服务器的操作。

    2.有足够的时间记录一些需要的信息操作。

    怎么办?!因为我们需要的时间可能会有点长,而默认情况下,iOS没有留给我们足够的时间。
    悲剧了……
    总需要有一个办法来解决~~~~
    向iOS申请,在后台完成一个Long-Running Task任务

    当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。

    如果你想在后台完成一个长期任务,就必须调用 UIApplication 的 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 借点时间。

    默认情况下,如果在这个期限内,长期任务没有被完成,iOS 将终止程序。

    怎么办?可以使用 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 再借点时间。

    既然是借时间,那么就需要有一些约定俗成的方式。
    先贴代码吧:
    1.项目的AppDelegate.h文件中
     
    声明一个 UIBackgroundTaskIdentifier ,相当于一个借据吧。告诉iOS,我们的程序将要借更多的时间来完成 Long-Running Task 任务。
     

    @property (nonatomic, unsafe_unretained) UIBackgroundTaskIdentifier backgroundTaskIdentifier;

    @property (nonatomic, strong) NSTimer *myTimer;

    2.项目的AppDelegate.m文件中
     
    1.注意在applicationDidEnterBackground方法中,完成借据的流程
     
    即:
    self. backgroundTaskIdentifier =[application beginBackgroundTaskWithExpirationHandler:^( void) {

            [self endBackgroundTask];

       }];

     
     

    // 当应用程序掉到后台时,执行该方法

    // 当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。

    // 如果你想在后台完成一个长期任务,就必须调用 UIApplication 的 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 借点时间。

    // 默认情况下,如果在这个期限内,长期任务没有被完成,iOS 将终止程序。

    // 怎么办?可以使用 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 再借点时间。

    - (void)applicationDidEnterBackground:(UIApplication *)application

    {

        // 使用这个方法来释放公共的资源、存储用户数据、停止我们定义的定时器(timers)、并且存储在程序终止前的相关信息。

        // 如果,我们的应用程序提供了后台执行的方法,那么,在程序退出时,这个方法将代替applicationWillTerminate方法的执行。

       

       

        // 标记一个长时间运行的后台任务将开始

        // 通过调试,发现,iOS给了我们额外的10分钟(600s)来执行这个任务。

        self.backgroundTaskIdentifier =[application beginBackgroundTaskWithExpirationHandler:^(void) {

            // 当应用程序留给后台的时间快要到结束时(应用程序留给后台执行的时间是有限的), 这个Block块将被执行

            // 我们需要在次Block块中执行一些清理工作。

            // 如果清理工作失败了,那么将导致程序挂掉

         

            // 清理工作需要在主线程中用同步的方式来进行

            [self endBackgroundTask];

      }];

       

        // 模拟一个Long-Running Task 

        self.myTimer =[NSTimer scheduledTimerWithTimeInterval:1.0f

                              target:self

                             selector:@selector(timerMethod:)     userInfo:nil

                             repeats:YES];

       

    }

    2.完成后,要告诉iOS,任务完成,提交完成申请“好借好还”:

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

           strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

         }

       });

    - (void) endBackgroundTask{

        dispatch_queue_t mainQueue = dispatch_get_main_queue();

      AppDelegate *weakSelf = self;

      dispatch_async(mainQueue, ^(void) {

         

        AppDelegate *strongSelf = weakSelf;

        if (strongSelf != nil){

          [strongSelf.myTimer invalidate];// 停止定时器

           

                // 每个对 beginBackgroundTaskWithExpirationHandler:方法的调用,必须要相应的调用 endBackgroundTask:方法。这样,来告诉应用程序你已经执行完成了。

                // 也就是说,我们向 iOS 要更多时间来完成一个任务,那么我们必须告诉 iOS 你什么时候能完成那个任务。

                // 也就是要告诉应用程序:“好借好还”嘛。

                // 标记指定的后台任务完成

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

          // 销毁后台任务标识符

          strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

        }

      });

    }

    // 模拟的一个 Long-Running Task 方法

    - (void) timerMethod:(NSTimer *)paramSender{

        // backgroundTimeRemaining 属性包含了程序留给的我们的时间

        NSTimeInterval backgroundTimeRemaining =[[UIApplication sharedApplication] backgroundTimeRemaining];

      if (backgroundTimeRemaining == DBL_MAX){

            NSLog(@"Background Time Remaining = Undetermined");

      } else {

            NSLog(@"Background Time Remaining = %.02f Seconds", backgroundTimeRemaining);

      }

    }

    3.记住,借和换必须成双成对!
    具体的解释,我也写在了方法中,如果有错误之处,还希望能够指正!谢谢!
    4.如果,程序提前完成了,也可以提前结束:

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

                self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

    向iOS申请,在后台无限时间
    经过证明,即使时执行Long-Running Task 任务,当程序被调到后台后,也是有时间限制的。一般为10分总(600s)。如何向程序申请无限时间呢?!
    那就欺骗iOS系统吧。让它感觉你的程序还是在运行。
    那就在后台用 AVAudioPlayer无限循环播放一个音频文件。
    呵呵,如果播放一个无声音的音频文件呢?!!
    步骤:
    1.在plish文件中加入背景播放的支持。
    加入项:Required background modes。并设置为:audio
    2.初始化一个AVAudioPlayer音频,并且无限制的播放下去。

    - (void)viewDidLoad

    {

        [super viewDidLoad];

       

        dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

      dispatch_async(dispatchQueue, ^(void) {

        NSError *audioSessionError = nil;

        AVAudioSession *audioSession = [AVAudioSession sharedInstance];

        if ([audioSession setCategory:AVAudioSessionCategoryPlayback error:&audioSessionError]){

                NSLog(@"Successfully set the audio session.");

        } else {

                NSLog(@"Could not set the audio session");

        }

         

         

        NSBundle *mainBundle = [NSBundle mainBundle];

        NSString *filePath = [mainBundle pathForResource:@"mySong" ofType:@"mp3"];

        NSData *fileData = [NSData dataWithContentsOfFile:filePath];

        NSError *error = nil;

        

        self.audioPlayer = [[AVAudioPlayer alloc] initWithData:fileData error:&error];

         

        if (self.audioPlayer != nil){

          self.audioPlayer.delegate = self;

          [self.audioPlayer setNumberOfLoops:-1];

           if ([self.audioPlayer prepareToPlay] && [self.audioPlayer play]){

                     NSLog(@"Successfully started playing...");

           } else {

             NSLog(@"Failed to play.");

           }

         } else {

                

         }

      });

    }

  • 相关阅读:
    unsupported jsonb version number 123
    如何在MPlayer上支持RTSP
    TDengine 时序数据库的 ADO.Net Core 提供程序 Maikebing.EntityFrameworkCore.Taos
    如何使用IoTSharp对接ModBus?
    如何从源码启动和编译IoTSharp
    Asp.Net Core 自动适应Windows服务、Linux服务、手动启动时的内容路径的扩展方法
    MQTTnet 的Asp.Net Core 认证事件的扩展
    Asp.Net Core 中利用QuartzHostedService 实现 Quartz 注入依赖 (DI)
    The remote certificate is invalid according to the validation procedure 远程证书验证无效
    settings插拔式源码
  • 原文地址:https://www.cnblogs.com/itlover2013/p/4208222.html
Copyright © 2011-2022 走看看