iOS-实现后台长时间运行
前言
一般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 = ;
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 = -;
}
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: 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 + ;
NSLog(@"_count = %ld",_count)
} /**
检测后台运行时间
*/
- (void)startAudioPlay{
_count = ;
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 对应的方法中,实现开启和停止后台运行即可!
iOS-实现后台长时间运行的更多相关文章
- iOS开发:保持程序在后台长时间运行
iOS开发:保持程序在后台长时间运行 2014 年 5 月 26 日 / NIVALXER / 0 COMMENTS iOS为了让设备尽量省电,减少不必要的开销,保持系统流畅,因而对后台机制采用墓碑式 ...
- iOS开发:后台运行以及保持程序在后台长时间运行
第一部分 1.先说说iOS 应用程序5个状态: 停止运行-应用程序已经终止,或者还未启动. 不活动-应用程序处于前台但不再接收事件(例如,用户在app处于活动时锁住了设备). 活动-app处于“使用中 ...
- 保持程序在后台长时间运行-b
iOS为了让设备尽量省电,减少不必要的开销,保持系统流畅,因而对后台机制采用墓碑式的“假后台”.除了系统官方极少数程序可以真后台,一般开发者开发出来的应用程序后台受到以下限制:1.用户按Home之后, ...
- iOS保持App真后台运行
https://www.jianshu.com/p/d466f2da0d33 在我看来,苹果系统与安卓系统最直观的区别就是后台处理方式了吧,安卓手机一旦开启了很多app放到后台,即使前台什么也不做,就 ...
- [ASP.NET Core 3框架揭秘] 服务承载系统[1]: 承载长时间运行的服务[上篇]
借助.NET Core提供的承载(Hosting)系统,我们可以将任意一个或者多个长时间运行(Long-Running)的服务寄宿或者承载于托管进程中.ASP.NET Core应用仅仅是该承载系统的一 ...
- phonegap ios插件开发及无限后台运行解决
1.首先开发插件:因为我的项目前需要所以要做(根据情况) 在项目的plugins文件中新建obj c文件.如 Demo,此时会产生出Demo.h和Demo.m两个文件. .h文件主要就是定义一些方法, ...
- 定时器解决js长时间运行脚本问题
一般地,单个js操作的运行时间不应超过100毫秒,否则的话,会影响用户体验,用户会认为自己与界面失去联系.而对于一些复杂的任务,可能无法在100ms内完成,甚至会突破浏览器限制(调用栈大小限制和长时间 ...
- ASP.NET 工作流:支持长时间运行操作的 Web 应用程序
ASP.NET 工作流 支持长时间运行操作的 Web 应用程序 Michael Kennedy 代码下载位置:MSDN 代码库 在线浏览代码 本文将介绍以下内容: 独立于进程的工作流 同步和异步活 ...
- android studio从1.5更新到2.0后terminal无法运行gradle命令,提示无法找到gradle命令
android studio从1.5更新到2.0后terminal无法运行gradle命令,提示无法找到gradle命令. 'gradle' 不是内部或外部命令,也不是可运行的程序 或批处理文件. 设 ...
随机推荐
- datatables插件提示Cannot reinitialise DataTable的解决办法
这个错误是由于重新设置数据源,又没有将原来的数据清空导致的. 网上有很多解决方案,试了都不管用. 最后找到一种方法,将原来的table销毁,再初始化. 方法是在datatable初始化的时候加入属性 ...
- 阿里巴巴Java开发手册与自己开发对照笔记
一编程规约 (一)命名风格 某些时候在命名常量的时候,会觉得太长而减少长度导致命名不清. 抽象类及测试类写得比较少. 这一点值得注意,在开发中,布尔变量我都是使用is开始. 关于包名和类名的单数和复数 ...
- Python学习—基础篇之文件操作
文件操作 文件操作也是编程中需要熟练掌握的技能,尤其是在后台接口编写和数据分析过程中,对各种类型的文件进行操作,获取文件信息或者对信息进行存储是十分重要的.本篇博客中将主要对常见的文本格式文件和Exc ...
- Python设计模式 - 创建型 - 单例模式(Singleton) - 十种
对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...
- equals的使用
源码:这里只是把Integer拿出来,String,Long 都一样 /** * Compares this object to the specified object. The result is ...
- 100-days: fourteen
Title: Face mask craze(面膜热) creates Korean(韩国) (a) billionaire with Goldman(高盛集团) backing face mask ...
- windbg获取打印
经常有QT MFC程序调用动态库无法查看内部打印 解决办法: 文件头部定义: #define UseDebugView #ifdef UseDebugView char g_Debug[256]; # ...
- Java18-java语法基础——集合框架
Java18-java语法基础——集合框架 一.什么是集合框架 1.集合框架:是为表示和操作集合而规定的一种统一的.标准的体系结构. 2.任何集合框架都包含三大块内容:对外的接口.接口的实现和对集合运 ...
- Spring Boot+Quartz实现一个实时管理的定时任务
转载 https://www.cnblogs.com/wujiwen/p/9615120.html 项目实践过程中碰到一个动态管理定时任务的需求:针对每个人员进行信息的定时更新,具体更新时间可随时调整 ...
- 批量屏蔽符合条件的IP地址,支持添加白名单,IP段,增量,大于指定次数的IP
批量屏蔽符合条件的IP地址,支持添加白名单,IP段,增量 大概的思路是利用sh,从日志中提取出来对应的IP地址,然后再交由python进行对比,判断,最终将需要添加至iptables列表中的IP写入到 ...