iOS实现后台长时间运行
程序员文章站
2022-04-29 21:17:29
前言
一般app在按下home键被挂起后,这时app的 backgroundtimeremaining 也就是后台运行时间大约只有3分钟,如果在退出app后,过十几二...
前言
一般app在按下home键被挂起后,这时app的 backgroundtimeremaining 也就是后台运行时间大约只有3分钟,如果在退出app后,过十几二十二分钟或者更长时间再回到app,app就会回到刚打开时的状态,也就是首页;有的项目在被挂起后需要在后台运行一段时间,使有足够的时间来完成与服务器对接的操作,或者需要一直运行的需求;如果需要,则在app被挂起后,申请后台,来延长后台运行时间。
app申请后台运行的方式有几种:
播放音乐
定位
newsstand downloads
fetch 等;
这里主要说下后台播放无声音乐(其实是不播放),采用哪种方式,对应勾选上图;我的项目中有音频播放需求,如果没有,那就找一个播放音频的理由,或者用其他方式实现。
实现
这里采用了两个单例:电话监控(xktelmanager)、后台运行(xkbgrunmanager),电话监控可以忽略,视情况而用;采用单例是为了方便管理;
xktelmanager.h
#import <foundation/foundation.h> @interface xktelmanager : nsobject ///是否在后台运行 @property (nonatomic,assign) bool inbackgroundrun; + (xktelmanager *)sharedmanager; /** 来电监听 */ - (void)startmonitor; @end
xktelmanager.m
#import "xktelmanager.h" #import "xkbgrunmanager.h" #import <coretelephony/ctcallcenter.h> #import <coretelephony/ctcall.h> static xktelmanager *_sharedmanger; @interface xktelmanager() @property (nonatomic, strong) ctcallcenter *callcenter; @end @implementation xktelmanager + (xktelmanager *)sharedmanager{ static dispatch_once_t oncetelsingle; dispatch_once(&oncetelsingle, ^{ if (!_sharedmanger) { _sharedmanger = [[xktelmanager alloc]init]; } }); return _sharedmanger; } - (instancetype)init{ self = [super init]; if (self) { _inbackgroundrun = no; } return self; } #pragma mark -********* 监听电话相关 - (void)startmonitor { __weak typeof(self) weakself = self; _callcenter = [[ctcallcenter alloc] init]; _callcenter.calleventhandler = ^(ctcall * call) { ///如果已经进入后台了,不做任何操作 if (weakself.inbackgroundrun) { return; } ///app未进入后台 if ([call.callstate isequaltostring:ctcallstatedisconnected]){ nslog(@"电话 --- 断开连接"); [[xkbgrunmanager sharedmanager] stopbgrun]; } else if ([call.callstate isequaltostring:ctcallstateconnected]){ nslog(@"电话 --- 接通"); } else if ([call.callstate isequaltostring:ctcallstateincoming]){ nslog(@"电话 --- 待接通"); [[xkbgrunmanager sharedmanager] startbgrun]; } else if ([call.callstate isequaltostring:ctcallstatedialing]){ nslog(@"电话 --- 拨号中"); [[xkbgrunmanager sharedmanager] startbgrun]; } else { nslog(@"电话 --- 无操作"); } }; } @end
xkbgrunmanager.h
#import <foundation/foundation.h> @interface xkbgrunmanager : nsobject + (xkbgrunmanager *)sharedmanager; /** 开启后台运行 */ - (void)startbgrun; /** 关闭后台运行 */ - (void)stopbgrun; @end
xkbgrunmanager.m
#import "xkbgrunmanager.h" ///循环时间 static nsinteger _circuladuration = 60; static xkbgrunmanager *_sharedmanger; @interface xkbgrunmanager() @property (nonatomic,assign) uibackgroundtaskidentifier task; ///后台播放 @property (nonatomic,strong) avaudioplayer *playerback; @property (nonatomic, strong) nstimer *timerad; ///用来打印测试 @property (nonatomic, strong) nstimer *timerlog; @property (nonatomic,assign) nsinteger count; @end @implementation xkbgrunmanager{ cfrunloopref _runloopref; dispatch_queue_t _queue; } + (xkbgrunmanager *)sharedmanager{ static dispatch_once_t oncerunsingle; dispatch_once(&oncerunsingle, ^{ if (!_sharedmanger) { _sharedmanger = [[xkbgrunmanager alloc]init]; } }); return _sharedmanger; } /// 重写init方法,初始化音乐文件 - (instancetype)init { if (self = [super init]) { [self setupaudiosession]; _queue = dispatch_queue_create("com.audio.inbackground", null); //静音文件 nsstring *filepath = [[nsbundle mainbundle] pathforresource:@"****" oftype:@"mp3"]; nsurl *fileurl = [[nsurl alloc] initfileurlwithpath:filepath]; self.playerback = [[avaudioplayer alloc] initwithcontentsofurl:fileurl error:nil]; [self.playerback preparetoplay]; // 0.0~1.0,默认为1.0 self.playerback.volume = 0.01; // 循环播放 self.playerback.numberofloops = -1; } return self; } - (void)setupaudiosession { // 新建audiosession会话 avaudiosession *audiosession = [avaudiosession sharedinstance]; // 设置后台播放 nserror *error = nil; [audiosession setcategory:avaudiosessioncategoryplayback withoptions:avaudiosessioncategoryoptionmixwithothers error:&error]; if (error) { nslog(@"error setcategory avaudiosession: %@", error); } nslog(@"%d", audiosession.isotheraudioplaying); nserror *activeseterror = nil; // 启动audiosession,如果一个前台app正在播放音频则可能会启动失败 [audiosession setactive:yes error:&activeseterror]; if (activeseterror) { nslog(@"error activating avaudiosession: %@", activeseterror); } } /** 启动后台运行 */ - (void)startbgrun{ [self.playerback play]; [self applyforbackgroundtask]; ///确保两个定时器同时进行 dispatch_async(_queue, ^{ self.timerlog = [[nstimer alloc] initwithfiredate:[nsdate date] interval:1 target:self selector:@selector(log) userinfo:nil repeats:yes]; self.timerad = [[nstimer alloc] initwithfiredate:[nsdate date] interval:_circuladuration target:self selector:@selector(startaudioplay) userinfo:nil repeats:yes]; _runloopref = cfrunloopgetcurrent(); [[nsrunloop currentrunloop] addtimer:self.timerad formode:nsdefaultrunloopmode]; [[nsrunloop currentrunloop] addtimer:self.timerlog formode:nsdefaultrunloopmode]; cfrunlooprun(); }); } /** 申请后台 */ - (void)applyforbackgroundtask{ _task =[[uiapplication sharedapplication] beginbackgroundtaskwithexpirationhandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ [[uiapplication sharedapplication] endbackgroundtask:_task]; _task = uibackgroundtaskinvalid; }); }]; } /** 打印 */ - (void)log{ _count = _count + 1; nslog(@"_count = %ld",_count) } /** 检测后台运行时间 */ - (void)startaudioplay{ _count = 0; dispatch_async(dispatch_get_main_queue(), ^{ if ([[uiapplication sharedapplication] backgroundtimeremaining] < 61.0) { nslog(@"后台快被杀死了"); [self.playerback play]; [self applyforbackgroundtask]; } else{ nslog(@"后台继续活跃呢"); }///再次执行播放器停止,后台一直不会播放音乐文件 [self.playerback stop]; }); } /** 停止后台运行 */ - (void)stopbgrun{ if (self.timerad) { cfrunloopstop(_runloopref); [self.timerlog invalidate]; self.timerlog = nil; // 关闭定时器即可 [self.timerad invalidate]; self.timerad = nil; [self.playerback stop]; } if (_task) { [[uiapplication sharedapplication] endbackgroundtask:_task]; _task = uibackgroundtaskinvalid; } } @end
最后在 appdelegate.m 对应的方法中,实现开启和停止后台运行即可!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。