当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS实现后台长时间运行

iOS实现后台长时间运行

2019年07月23日  | 移动技术网IT编程  | 我要评论

凶猛鱼类,捍卫战士q9088,葡萄酒制作

前言

一般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 对应的方法中,实现开启和停止后台运行即可!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网