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

iOS-实现后台长时间运行

2018年10月11日  | 移动技术网移动技术  | 我要评论

前言

一般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 监控程序挂起的方法中:

/// 后台运行
- (void)applicationdidenterbackground:(uiapplication *)application {
    nslog(@"程序挂起了");
    [xktelmanager sharedmanager].inbackgroundrun = yes;
    [[xkbgrunmanager sharedmanager] startbgrun];
}

在 appdelegate.m 监控回到app的方法中:

/// 回到app
- (void)applicationwillenterforeground:(uiapplication *)application {
    [xktelmanager sharedmanager].inbackgroundrun = no;
    [[xkbgrunmanager sharedmanager] stopbgrun];
}

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网