美文网首页
iOS 保证定时器进入后台依然运行

iOS 保证定时器进入后台依然运行

作者: 果哥爸 | 来源:发表于2022-06-27 00:29 被阅读0次

    一. 问题背景

    最近项目中有个定时器计时实时更新等车的时长,因为项目里面进入后台是有执行一些任务的操作,因此如果进入后台时间不长,是定时器是不会暂停的,但如果进入后台时间,超过20s以上,定时器就暂停,回到前台重新开始倒计时,这时候等车的时长会出现不准的情况。

    二. 问题原因

    经验证NSTimer,CADisplayLinkdispatch_source_t,三个定时器,在进入到后台的时候,都会暂停,等到返回前台的时候,才会继续回调。

    timer.gif

    看了一些博客说加上后台任务执行这句话可以保证App进入后台,定时器不会暂停,依然继续执行

    [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    

    经验证,后台执行任务也将暂停延迟,还是没办法解决App长时间进入后台,定时器暂停问题。

    我们通过监听mainRunLoop回调可以发现,当App进入到后台,mainRunLoop进入了休眠,当App回到前台,mainRunLoop重新唤醒继续执行。

    main_runloop_timer.gif

    因此我再想,如果在App进入后台的时候,将已经睡眠的mainRunLoop重新唤醒,是不是就可以保证定时器的不暂停,持续运行。

    - (void)didEnterBackground {
        NSLog(@"--------------------------didEnterBackground");
        [[NSRunLoop mainRunLoop] run];
    }
    
    
    main_runloop_background_run_timer.gif

    经验证,结果如猜想一样,在App进入后台,重新唤醒mainRunLoop,可以保证定时器不暂停,可以一直运行。

    因此这里我推断,因为我们定时器的回调任务是添加到主队列,由于进入后台,mainRunLoop进入休眠,导致主线程没有去执行主队列的的任务,因此导致定时器没有回调。

    那如果我在子线程开启定时器倒计时,然后通过runloop保活这个子线程,监听这个子线程的runloop回调,发现当App进入后台,子线程的runloop也进入休眠,这时候子线程的定时器也不再回调.

    /// 开启 子线程 倒计时
    - (void)startSubThreadNormalTimer {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            self.countTimer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:true
                                                                block:^(NSTimer * _Nonnull timer) {
                NSLog(@"--------------------------subThread countDownTimer");
            }];
            [[NSRunLoop currentRunLoop] addTimer:self.countTimer forMode:NSRunLoopCommonModes];
            [self startCurRunloopMonitor];
            self.subThreadRunloop = [NSRunLoop currentRunLoop];
            [[NSRunLoop currentRunLoop] run];
        });
    }
    
    sub_thread_runloop_background_run_timer.gif

    这时候,我在App进入后台,单独将子线程的runloop唤醒,发现子线程的定时器依然不会进行回调。

    - (void)didEnterBackground {
        NSLog(@"--------------------------didEnterBackground");
        [self.subThreadRunloop run];
    //    [[NSRunLoop mainRunLoop] run];
    }
    
    sub_thread_runloop_background_run.gif

    但是如果在App进入后台,单独将主线程的mainRunloop唤醒,发现子线程的定时器就可以正常执行。

    sub_thread_runloop_background_main_run.gif

    这个现象背后的本质原理是怎样,我找了相关资料,也跟朋友探讨过,依然没有得到一个合理的解释。知道的朋友,可以留言通知下。

    三. 结论

    如果想让App进入后台,定时器依然能继续执行,最有效的办法,就是监听App进入后台的通知,在App进入后台之后,唤醒主线程的mainRunloop,也就是加上这句:

    [[NSRunLoop mainRunLoop] run];
    

    相关文章

      网友评论

          本文标题:iOS 保证定时器进入后台依然运行

          本文链接:https://www.haomeiwen.com/subject/mnfwprtx.html