iOS 后台挂起的一些坑

特别说明:
后台状态:当前app如果不是作为屏幕中的第一层,呈现显示给用户,那么此时app就是后台状态。
  •   锁屏(包括:当前应用下锁屏、其他应用下锁屏、桌面锁屏)
  •   用户在使用其他应用app2,当前 app1 虽然没有上滑kill掉,但是屏幕中的第一层显示的是app2,  那么app1就是后台了。
  •   部分系统甚至包括:拉下系统菜单 (即:显示最近通知的菜单),上拉系统菜单 (即:显示WiFi和蓝牙的菜单)。此时app 也是后台。

 

挂起状态:当前app后台状态,但是不一定挂起,挂起就是关于app的一切代码都不再运行了。

  • 比如后台播放音乐,传送文件等。
  • 比如本文中的方法可以申请几分钟时间,让代码接着运行。

从测试实践来看,如果app进入后台状态,一般情况下是很快就会被挂起的,也就是进入后台状态后,里面代码运行马上就停止了。

 

 

遇到类似的情况基本上就行不通了:

例如:

    多个设备共用一个打印机: 通过socket 让设备1 作为服务器(此时连着打印机),

    设备2 作为客户端没连接打印机(打印机只能连一个设备),设备2 把一段文字发给设备1,让设备1 通过打印机打出来。这样行不通的。

 

 现象:

   作为服务器的设备1 随时可能进入后台,进入后台后早晚会挂起,挂起后什么信息都无法,唤醒代码。

  (此时不考虑 设备1, 设备2, 打印机 三者蓝牙互联的方案,其实也有问题。蓝牙同时连接两个,传数据的时候一般会断掉一个)。

 

原因:iOS 系统要保证流畅(当内存不够就会优先kill掉没在使用中的后台app),更不会允许后台app偷偷摸摸的不停运行代码浪费资源。

 

 

 

解决思路有二:

方法1:当app刚进入后台,可以通过特定方法申请几分钟的时间接着运行代码,但不会太久,就几分钟而已,

      而且时间到了,app还会被杀死,上线的时候还需要对此功能,特别的向苹果说明,否则会被拒绝。

方法2:如果需要做的是语音类的服务,那么幸运了(例如后台播放一段音乐什么的)。可以开通 VOIP (Voice over Internet Protocol)语音服务。

    voip这个用苹果pushkit 框架接通就行了,这个东西是优于 远程推送 的,他可以在服务器push过来消息以后,不用 用户操作,

    直接运行iOS app里面的代码回调(远程推送是不会唤醒代码的,只有当用户点击推动横幅,唤醒app后,代码才会有回调,也就是需要用户操作)。

    注意:如果用开通了 voip 但是却做与voip无关的事情,苹果也是不会允许的,会被拒绝哦。 比如下文后台默默播放没声音的音乐。

 

 

 

我们知道,到我们程序从前台退到后台(安home)键后,将执行程序的委托方法。

// 当应用程序掉到后台时,执行该方法

1
2
- (void)applicationDidEnterBackground:(UIApplication *)application {
}
我们已经知道:

当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。

我们假设有这么一种情况:
当我们的应用程序从前台被送到了后台。
这时候,我们的程序将执行委托方法 applicationDidEnterBackground。但是,这时候,应用程序只给了我们可怜的一点点时间(也就是秒级别的)来处理东西,然后,所有的线程都被挂起了。
而实际中,我们可能需要更长的时间来完成我们的需要的必要操作:

1.我们需要在应用程序推到后台时,能够有足够的时间来完成将数据保存到远程服务器的操作。

2.有足够的时间记录一些需要的信息操作。

怎么办?!因为我们需要的时间可能会有点长,而默认情况下,iOS没有留给我们足够的时间。
悲剧了……
总需要有一个办法来解决~~~~
向iOS申请,在后台完成一个Long-Running Task任务

当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。

如果你想在后台完成一个长期任务,就必须调用 UIApplication 的 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 借点时间。

默认情况下,如果在这个期限内,长期任务没有被完成,iOS 将终止程序。

怎么办?可以使用 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 再借点时间。

既然是借时间,那么就需要有一些约定俗成的方式。
先贴代码吧:
1.项目的AppDelegate.h文件中
声明一个 UIBackgroundTaskIdentifier ,相当于一个借据吧。告诉iOS,我们的程序将要借更多的时间来完成 Long-Running Task 任务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
self. backgroundTaskIdentifier =[application beginBackgroundTaskWithExpirationHandler:^( void) {
        [self endBackgroundTask];
   }];
// 当应用程序掉到后台时,执行该方法
  
// 当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。
// 如果你想在后台完成一个长期任务,就必须调用 UIApplication 的 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 借点时间。
// 默认情况下,如果在这个期限内,长期任务没有被完成,iOS 将终止程序。
// 怎么办?可以使用 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 再借点时间。
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // 使用这个方法来释放公共的资源、存储用户数据、停止我们定义的定时器(timers)、并且存储在程序终止前的相关信息。
    // 如果,我们的应用程序提供了后台执行的方法,那么,在程序退出时,这个方法将代替applicationWillTerminate方法的执行。
    
    
    // 标记一个长时间运行的后台任务将开始
    // 通过调试,发现,iOS给了我们额外的10分钟(600s)来执行这个任务。
    self.backgroundTaskIdentifier =[application beginBackgroundTaskWithExpirationHandler:^(void) {
  
        // 当应用程序留给后台的时间快要到结束时(应用程序留给后台执行的时间是有限的), 这个Block块将被执行
        // 我们需要在次Block块中执行一些清理工作。
        // 如果清理工作失败了,那么将导致程序挂掉
      
        // 清理工作需要在主线程中用同步的方式来进行
        [self endBackgroundTask];
  }];
    
    // 模拟一个Long-Running Task
    self.myTimer =[NSTimer scheduledTimerWithTimeInterval:1.0f
                          target:self
                         selector:@selector(timerMethod:)     userInfo:nil
                         repeats:YES];
    
}

  2.完成后,要告诉iOS,任务完成,提交完成申请“好借好还”:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
       strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
     }
   });
- (void) endBackgroundTask{
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
  AppDelegate *weakSelf = self;
  dispatch_async(mainQueue, ^(void) {
      
    AppDelegate *strongSelf = weakSelf;
    if (strongSelf != nil){
      [strongSelf.myTimer invalidate];// 停止定时器
        
            // 每个对 beginBackgroundTaskWithExpirationHandler:方法的调用,必须要相应的调用 endBackgroundTask:方法。这样,来告诉应用程序你已经执行完成了。
            // 也就是说,我们向 iOS 要更多时间来完成一个任务,那么我们必须告诉 iOS 你什么时候能完成那个任务。
            // 也就是要告诉应用程序:“好借好还”嘛。
            // 标记指定的后台任务完成
            [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
      // 销毁后台任务标识符
      strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    }
  });
}
// 模拟的一个 Long-Running Task 方法
- (void) timerMethod:(NSTimer *)paramSender{
    // backgroundTimeRemaining 属性包含了程序留给的我们的时间
    NSTimeInterval backgroundTimeRemaining =[[UIApplication sharedApplication] backgroundTimeRemaining];
  if (backgroundTimeRemaining == DBL_MAX){
        NSLog(@"Background Time Remaining = Undetermined");
  else {
        NSLog(@"Background Time Remaining = %.02f Seconds", backgroundTimeRemaining);
  }
}

 

  

3.记住,借和换必须成双成对,如果,程序提前完成了,也可以提前结束:
1
2
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
           self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

  

 

向iOS申请,在后台无限时间
经过证明,即使时执行Long-Running Task 任务,当程序被调到后台后,也是有时间限制的。一般为10分总(600s)。如何向程序申请无限时间呢?!
那就欺骗iOS系统吧。让它感觉你的程序还是在运行。
那就在后台用 AVAudioPlayer无限循环播放一个音频文件。
呵呵,如果播放一个无声音的音频文件呢?!!
步骤:
1.在plish文件中加入背景播放的支持。
加入项:Required background modes。并设置为:audio
2.初始化一个AVAudioPlayer音频,并且无限制的播放下去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  dispatch_async(dispatchQueue, ^(void) {
    NSError *audioSessionError = nil;
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    if ([audioSession setCategory:AVAudioSessionCategoryPlayback error:&audioSessionError]){
            NSLog(@"Successfully set the audio session.");
    else {
            NSLog(@"Could not set the audio session");
    }
      
      
    NSBundle *mainBundle = [NSBundle mainBundle];
    NSString *filePath = [mainBundle pathForResource:@"mySong" ofType:@"mp3"];
    NSData *fileData = [NSData dataWithContentsOfFile:filePath];
    NSError *error = nil;
     
    self.audioPlayer = [[AVAudioPlayer alloc] initWithData:fileData error:&error];
      
    if (self.audioPlayer != nil){
      self.audioPlayer.delegate = self;
  
      [self.audioPlayer setNumberOfLoops:-1];
       if ([self.audioPlayer prepareToPlay] && [self.audioPlayer play]){
                 NSLog(@"Successfully started playing...");
       else {
         NSLog(@"Failed to play.");
       }
     else {
             
     }
  });
}

  

蓝牙传数据:后台模式,app被杀死时候的处理方式:

http://blog.csdn.net/swibyn/article/details/52096646 

 

转载于:https://www.cnblogs.com/Ghosgt/p/8745969.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OSGI 生命周期

1 生命周期管理 对于非模块化应用,生命周期将应用作为一个整体来操作;而对于模块化应用,则可以以细粒度的方式来管理应用的某一个独立部分。OSGi生命周期管理 OSGi生命周期层有两种不同的作用: 在应用程序外部,定义了对…

tomcat+nginx+redis实现均衡负载、session共享

在项目运营时,我们都会遇到一个问题,项目需要更新时,我们可能需先暂时关闭下服务器来更新。但这可能会出现一些状况:1.用户还在操作,被强迫终止了(我们可以看日志等没人操作的时候更新,但总可能会有万一)2.不知道的用户…

洛谷 P3184 [USACO16DEC]Counting Haybales数草垛

洛谷 P3184 [USACO16DEC]Counting Haybales数草垛 题目描述 Farmer John has just arranged his NN haybales (1 \leq N \leq 100,0001≤N≤100,000 ) at various points along the one-dimensional road running across his farm. To make sure they are spaced out appropria…

关于笔试的一些博客

这里收集了一些阿里的网上笔试题目 阿里笔试题(2015)持续更新中腾讯阿里实习生招聘笔试总结阿里2014研发实习生笔试题解析【阿里】算法工程师笔试题整理(13&14年)【阿里】算法工程师笔试【2015.04.02】转载于:https://www.cnblogs.com/chen310/p/438…

Entity Framework 6 Recipes 2nd Edition(13-2)译 - 用实体键获取一个单独的实体

问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Painting(绘画)类型的实体,如Figure 13-2所示: Figure 13-2. The Painting entity type in our model 在代码In Listi…

C#心得与经验(二)

本周学到很多C#关于Interface, Array的知识&#xff0c;在这里简单复习一下几个易混的地方&#xff0c;重在理解。 一、Interface 使用as来避免多态时没有接口的Exception&#xff1a; Document [] folder new Document[5]; for (int i 0; i < 5; i) {if (i % 2 0){fold…

java实例化对象

摘要&#xff1a;分享牛&#xff0c;分享牛分享&#xff0c;java类加载机制&#xff0c;java实例化对象&#xff0c;java实例化对象机制&#xff0c;java基础。 java是如何实例化对象的呢&#xff1f;以及实例化对象的先后顺序是什么&#xff1f;下面我们以测试的方式说明. 1.1…

项目总结(3.28)

项目是用vuewebpackelementUI 完成的。虽然没有什么深奥的技术和难点&#xff0c;但是有些细节还是值得注意的。 1、满足不同屏幕尺寸下缩放全屏显示。 单单只靠宽度、高度百分比是不可以实现的&#xff0c;比如如果宽度设置百分比&#xff0c;当屏幕宽度比较小时&#xff0c;这…

Android开发删除短信

本人一直有一个需求&#xff0c;想要手机自动拦截黑名单里联系人的信息并自动删除这些短信&#xff0c;手机管家之类的软件可以拦截但是没找到能删除这些短信的&#xff0c;于是就萌生了想自己写一个android软件的想法。 加上物联网的兴起&#xff0c;安卓设备开发肯定前景很好…

让你提升命令行效率的 Bash 快捷键 [完整版]

生活在 Bash shell 中&#xff0c;熟记以下快捷键&#xff0c;将极大的提高你的命令行操作效率。 编辑命令 Ctrl a &#xff1a;移到命令行首Ctrl e &#xff1a;移到命令行尾Ctrl f &#xff1a;按字符前移&#xff08;右向&#xff09;Ctrl b &#xff1a;按字符后移&…

Okhttp的封装和回调

public class HttpUtil {static HttpUtil util;private final OkHttpClient client;// 私有化构造方法private HttpUtil(){client new OkHttpClient();}public static HttpUtil getInstance(){if(util null){synchronized (HttpUtil.class){util new HttpUtil();}}return u…

将Session写入Memcache

通过session_set_save_handler()方法自定义Session写入Memcache 1 <?php 2 class MemSession{3 private static $handler null;4 private static $lifetime null;5 private static $time null;6 const MS session;7 8 …

微软解决方案全景

我遇到过一家企业的CTO&#xff0c;做过很多知名大企业的信息化规划设计&#xff0c;但他基本是一个Like Unix派系的技术&#xff0c;而我是一个Microsoft派系的技术&#xff0c;在这里我不是讨论哪个派系的技术好和不好&#xff0c;我只是在和他的聊天中发现了一些非常有意思的…

Going Dutch BAPC( 状态转移DP)

题目描述 You and your friends have just returned from a beautiful vacation in the mountains of the Netherlands. When on vacation, it’s annoying to split the bill on every expense every time, so you just kept all the receipts from the vacation, and wrote d…

查询指定目录下的文件中是否包含指定字符串

cd /etc/apache2/site-enable/rep -rl test.com ./*意为查询apache虚拟主机中是否包含test.com的域名转载于:https://blog.51cto.com/linuxtips/1773938

使用 C# 编程对RTF文档的支持

http://www.68design.net/Development/Aspnet/Basis-AspNet/26011-1.html 转载于:https://www.cnblogs.com/faxian/p/4402910.html

algorand共识协议_【Filecoin】理解预期共识 - 及它的优缺点

摘 要预期共识就是上帝掷飞镖预期共识的优点在于简单&#xff0c;而且每一次选举胜出者数量的平均数为1但预期共识不能保证每次选举的胜出者数量&#xff0c;这是其最大的问题期待有更好的基于可验证随机函数的共识算法出现&#xff0c;设计者可获得20万美金奖赏预期共识 就是 …

Android软件安全与逆向分析之Dalvik

注意点 首先&#xff0c;让我们来思考下面几个问题&#xff1a; 什么是Dalvik虚拟机? Dalvik VM与JVM有什么区别&#xff1f; Dalvik VM有什么新的特点&#xff1f; Dalvik VM的架构是怎么样的&#xff1f; 首先&#xff0c;我得承认第一个问题问得很傻&#xff1a;什么是Dalv…

在java中实现日期类型和字符串类型的转换大全(Date String Timestamp Datetime)

用Timestamp来记录日期时间还是很方便的&#xff0c;但有时候显示的时候是不需要小数位后面的毫秒的&#xff0c;这样就需要在转换为String时重新定义格式。 Date、String、Timestamp之间的转换&#xff01; [java] view plaincopyprint? public static void main(String[] ar…

c++ 多个线程操作socket要同步吗_基础知识深化:NIO优化原理和Tomcat线程模型

1、I/O阻塞书上说BIO、NIO等都属于I/O模型&#xff0c;但是I/O模型这个范围有点含糊&#xff0c;我为此走了不少弯路。我们日常开发过程中涉及到NIO模型应用&#xff0c;如Tomcat、Netty中等线程模型&#xff0c;可以直接将其视为 网络I/O模型 。本文还是在基础篇章中介绍几种I…