美文网首页iOS开发记录为了更好的活着iOS进阶之路
iOS"伪后台"机制下如何保持APP一直运行

iOS"伪后台"机制下如何保持APP一直运行

作者: flightlessBirdT | 来源:发表于2017-03-01 20:52 被阅读9488次

最近在做番茄钟的功能。首先简单介绍一下番茄钟吧,就是25分钟工作番茄工作法。先说一下** 番茄工作法 **:

番茄工作法是简单易行的时间管理方法,是由弗朗西斯科·西里洛于1992年创立的一种相对于GTD更微观的时间管理方法。
使用番茄工作法,选择一个待完成的任务,将番茄时间设为25分钟,专注工作,中途不允许做任何与该任务无关的事,直到番茄时钟响起,然后在纸上画一个X短暂休息一下(5分钟就行),每4个番茄时段多休息一会儿。
番茄工作法极大地提高了工作的效率,还会有意想不到的成就感。

那么功能就相当于一个25分钟的闹钟,可以播放背景音乐,到点给用户提醒。

功能听起来很简单是不是?其实挺多坑的。

开发过程中遇到了2个问题。

  1. 因为番茄钟是25分钟,那么当用户开启番茄钟后很可能在中途就将APP切换到了后台,那么几分钟程序就会被系统kill掉。
  2. 当用户开启番茄钟的背景音乐时,APP切换到后台或者锁屏状态时,音乐都会立即停止播放。

OK,下面我们一步一步来分析并解决这两个问题。

** 首先要理解iOS系统的后台机制 **

我们都知道,苹果对APP占用硬件资源管的很严,更不要说应用后台时候的资源占用了。正常情况下,使用应用时,APP从硬盘加载到内存,开始工作;当用户按下home键,APP便被挂起,依然驻留在内存中,这种状态下,不调用苹果已开放的几种后台方法,程序便不会运行;如果在这个时候,使程序继续运行,则为后台状态;如果当前内存将要不够用时,系统会自动把之前挂起状态下的APP请出内存。所以我们看到,有些时候打开APP时,还是上次退出时的那个页面那些数据,有时则是重新从闪屏进入。

iOS系统后台机制大概可以分为5种状态

  • Not Running:APP没有启动,也没有后台运行。
  • Active:用户正在使用APP,比如说我们聊微信看网页的时候,APP就处于Active状态。
  • Inactive:这是一个过渡的状态,APP虽然打开了,但是用户没有跟APP有任何互动操作。
  • Background:APP在后台运行,微信会在没有打开的时候接收消息。
  • Suspended:APP虽然在后台运行,但是处于休眠状态,只占用一点内存。

** 那么我需要的是Background模式。即APP在后台运行同时保持程序active的状态 **

首先去xCode里面设置。到info.plist中添加以下信息:

Snip20170301_13.png

然后到Capabilities里面打开后台模式,并根据项目的要求勾选对应的功能。我这里只需要保持后台运行并且播放背景音乐及通知功能。所以就勾选了第一个和最后一个

Snip20170301_14.png

以上这两步是告诉系统我这个APP支持后台模式,对应的环境为音频环境。

可是到这一步,APP还是不能长时间运行到后台。

为什么?我们思考一下。我们让程序支持了后台运行的模式。那么我们是不是还需要系统知道我们的程序要在后台运行多久呢?我们需要告诉系统我们期望APP在后台存活的时间。

首先声明一个属性

 @property (nonatomic, assign) UIBackgroundTaskIdentifier bgTask;

在进入后台的时候通过AppDelegate里面的方法:

-(void)applicationDidEnterBackground:(UIApplication *)application{
  [ self comeToBackgroundMode];
}

-(void)comeToBackgroundMode{
    //初始化一个后台任务BackgroundTask,这个后台任务的作用就是告诉系统当前app在后台有任务处理,需要时间
    UIApplication*  app = [UIApplication sharedApplication];
    self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:self.bgTask];
    self.bgTask = UIBackgroundTaskInvalid;
    }];
    //开启定时器 不断向系统请求后台任务执行的时间
    self.timer = [NSTimer scheduledTimerWithTimeInterval:25.0 target:self selector:@selector(applyForMoreTime) userInfo:nil repeats:YES];
    [self.timer fire];
}

-(void)applyForMoreTime {
   //如果系统给的剩余时间小于60秒 就终止当前的后台任务,再重新初始化一个后台任务,重新让系统分配时间,这样一直循环下去,保持APP在后台一直处于active状态。
    if ([UIApplication sharedApplication].backgroundTimeRemaining < 60) {
    [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];
    }
}

现在就可以让我们的APP一直运行在后台啦!总结下来的思路就是:通过一个后台任务(这个任务我们也不用管,它存在的意义就是和系统去请求后台运行的一定的时间),这个时间我们不知道也不用去管,我们可以通过该时间还剩下多少判断是否继续请求时间,如此循环,我们就可以不断的请求时间来保持我们的app一直运行在后台。

接下来解决音乐在后台模式(切换到后台或者锁屏状态)下停止播放的问题。

其实很简单。

//设置后台模式和锁屏模式下依然能够播放
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];

//初始化播放器和两个音频(一个有声 一个无声)
NSURL *urlSound = [[NSURL alloc]initWithString:[[NSBundle mainBundle]pathForResource:@"pomodoSound" ofType:@"m4a"]];
playerSound = [[AVAudioPlayer alloc] initWithContentsOfURL:urlSound error:&playerError];
NSURL *urlNoSound = [[NSURL alloc]initWithString:[[NSBundle mainBundle]pathForResource:@"backSound" ofType:@"mp3"]];
playerNoSound = [[AVAudioPlayer alloc] initWithContentsOfURL:urlNoSound error:&playerError];

playerSound.numberOfLoops = -1;
playerNoSound.numberOfLoops = -1;

player = playerSound;
[player play];

下面解释一下AVAudioSession的一些设置参数

  • NSString *const AVAudioSessionCategoryAmbient;
    静音模式或者锁屏下不再播放音乐,和其他app声音混合。
  • NSString *const AVAudioSessionCategorySoloAmbient;
    默认模式,静音模式或者锁屏下不再播放音乐,不和其他app声音混合。
  • NSString *const AVAudioSessionCategoryPlayback;
    表示对于用户切换静音模式或者锁屏 都不理睬,继续播放音乐。并且不播放来自其他app的音乐
  • NSString *const AVAudioSessionCategoryRecord;
    不播放音乐,锁屏状态继续录音
  • NSString *const AVAudioSessionCategoryPlayAndRecord;
    播放音乐,并录音

Demo地址:https://github.com/BoYangZuo/KeepAppActive

** 欢迎star!**

相关文章

网友评论

  • PGOne爱吃饺子:好像真的不行,切换到后台三分钟就停止了
  • ac2fd060dae9:if ([UIApplication sharedApplication].backgroundTimeRemaining < 60) { 这个执行一次后,计时器 makeMoreTime 方法 就不执行了。后台播放的音乐 也会停止。
  • 逃跑计划sp:BackgroundTask,iOS7以后 每次向系统申请只能拿到180s的运行时间,不管申请多少次最多加起来一共只有600s 。 你这边测出来的长久运行 是audio的原因。
  • 半缘修道半缘君_6e45:楼主假如不勾选background modes里面的两个选项 ,你上面的方法是不是不能保证程序一直运行,我试了下,大概只能运行3分钟
    半缘修道半缘君_6e45:前后台切换要销毁timer和task
    不是特别闷骚的三石:https://www.jianshu.com/p/81cd082336f0
  • vision_colion:iOS 11之前可行,iOS 11就不行了,后台只能保持三分钟
    flightlessBirdT:@vision_colion 多找找你自己的问题。我的线上程序没有一个用户反馈你这种情况 我自己测后台放置半小时也没问题。
    vision_colion:@flightlessBirdT 兄得,我用你代码跑的,内存没问题,就开了你这个demo
    flightlessBirdT:@vision_colion 亲测可以。如果不是手机内存占用过多导致程序被后台回收了这种极端情况 那么就是你的代码有问题。
  • 新地球说着一口陌生腔调:为什么要用2个? 一个无声 一个有声?
  • 陈藩:这个方法,去年上半年使用是可以的,去年下半年之后就不行了。请问如果不播放无声音乐,要保持后台运行有方法么。
  • 落寞King:请问,如果只通过后台任务申请,不播放无声音频,可以达到后台不被杀死的效果吗?
  • leftwater:如何在not running下继续跑了 。。比如定位
  • f00b80523ba0:大神!
  • 阿龍飛:有个问题请教一下:让app一直在后台活着(运行)播放假音乐(无声),,,审核该如何避免不给通过
    flightlessBirdT:@阿龍飛 文中有代码呀。两个音频,一个无声,一个有声。
    阿龍飛:@flightlessBirdT你怎么初始化播放器和两个音频(一个有声 一个无声)
    flightlessBirdT:@阿龍飛 一般情况下 你在xcode里设置了本文所写的两个选项,后台播放无声文件审核是没问题的。我上一个版本就是这么做的。
  • 茄子_Apple:有个问题,重新初始化一个任务后,系统分配的任务后台运行的时间是多少?
    flightlessBirdT:@茄子_Apple 可以的。
    茄子_Apple:@flightlessBirdT 还想请教一下,这样的话审核能过吗?
    flightlessBirdT:这个写的很清楚了。这个时间我不知道。我只是通过还剩下多少时间判断是否继续请求下一个任务的时间。如果你需要确切时间的话,可以断点测一下,是能够推算出来的。

本文标题:iOS"伪后台"机制下如何保持APP一直运行

本文链接:https://www.haomeiwen.com/subject/acycgttx.html