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

2022-06-27  本文已影响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];
上一篇下一篇

猜你喜欢

热点阅读