相信很多人都遇到过这样的需求,就是想让APP保活。在手机系统日趋完善的阶段,安卓手机都很难实现保活了,更不用说以性能著称的苹果了,对吧。
众所周知,APP在压入后台以后会活一段时间,达到一定时间、或者内存占用系统过多的时候会被系统自动给kill掉。
静默推送
正常的推送网上博客和Demo一大堆,就不在这里赘述了。
回归正题,APP在活的时候,我们可以通过静默推送来实现APP在后台做一个网络请求,然后更新内部数据。如何静默推送其实前端是不需要考虑的,后台那边呢,其实和普通推送也没多大区别,就是alert那几个字段在推送的时候不传即可,做iOS开发的小伙伴都知道,下面这个方法是在收到消息的时候执行的,而且看文档也说,静默推送的时候,APP在后台也可以执行这个方法,然后大家就都开心了,可是使用过程中却是啪啪打脸,为什么集成好以后,APP在前台的时候没有问题,而且普通的消息推送什么的都正常,就是压到后台就不执行了呢?是不是苹果爸爸逗我们玩儿?
其实,想在后台做一些事情的话,需要再info.plist里声明一下,然后才能获取权限的:key:Required background modes 代表在后台需要做的事情,对应数组,App downloads content from the network,后台情况下可以访问网络,App downloads content in response to push notifications,后台情况下可以处理通知,也就是推送的消息,即下面的方法。如果和本地推送区分,那么让后台加一个字段去判断就OK了
info.plist中字段:
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
稍等收到推送后执行的方法:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
可是APP被手动或者系统自动kill掉以后怎么办呢,APP都死了,那里面的方法不执行也没毛病啊。别慌张,苹果官方还有一个神奇的东西----VoIP。
VoIP是官方支持网络电话的一套API,它是可以在APP被kill掉的状态下被唤醒的,唤醒后APP在后台是可以做一些事情的,这样就实现了很多需求。那么接下来就和大家说说VoIP该怎么样使用吧。
VoIP和推送有点相似,唤醒APP当然是苹果的服务器做的,所以我们需要VoIP证书,所以在集成VoIP之前需要制作VoIP证书
CSR文件
首先们要有生成一个Certificate Signing Request(CSR)的请求文件
在应用程序里的使用工具中找到钥匙串访问
。
这里邮箱和常用名写上,邮箱是必填的,常用名可以不写,但是!!!如果不写的话,在吧csr文件上传时候,哭死你,我之前的博客中有些,有兴趣的同学可以去看看。
到这里点击完成后我们会在桌面上看到一个CertificateSigningRequest.certSigningRequest
的请求文件,也就是我们说的CSR文件。在我们生成CSR文件的同时,会在钥匙串访问中生成一对秘钥,名称为刚才我们填写的常用名,制作证书的时候和推送证书一样,只不过在以下选项中选择VoIP就好了,然后我们就得到cer证书了,推送的话,大部分平台让我们提供P12证书,所以很多时候我们用cer证书去发布P12证书就OK了,可是如果自己用后台去做这一块儿的话,需要把P12证书转成pem证书才可以的。(证书的制作我这两天抽时间再写一篇博客来和大家分享)
接下来写代码吧
我们需要先引入静态库PushKit.framework
导入头文件 #import <PushKit/PushKit.h>
/**
* 注册VoIP
*/
- (void)registerVoipNotifications {
PKPushRegistry *voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
delegate = self;
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
NSLog(@"VoIP registered");
}
#pragma mark - PKPushRegistryDelegate
-(void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
NSString *str = [NSString stringWithFormat:@"%@",credentials.token];
NSString *_tokenStr = [[[str stringByReplacingOccurrencesOfString:@"<" withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" "withString:@""];
NSLog(@"device_token is %@" , _tokenStr);
NSLog(@"本地存储deviceToken = %@", _tokenStr);
if ([QPRooter isDevoteToken:_tokenStr]) {
//这里进行的操作,是将Device Token发送到服务端,因为服务器要激活某个APP的话,是需要设备token的,我们在这里把token给服务器并且保存下来就OK了
self requestDeviceToken:_tokenStr];
}
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type {
//激活APP后,会执行这个方法,而且在 payload 中带上后台给的参数,这里我们就可以利用不同的参数去做我们想做的事情了
NSLog(@"收到消息");
NSDictionary * dic = payload.dictionaryPayload;
NSLog(@"dic %@",dic);
}
前端实现了,那么后台呢?其实后台这边使用中和推送是完全一样的,只要把推送证书换成VoIP证书就好了。是不是很简单呢,不过到这里还不算完,同样是在后台,所以我们要在上面说过的info.plist里面加上相关字段,App provides Voice over IP services,好了,大功告成了,我们要的功能实现了,可这一下就到我的盲区了,功能实现了并不代表着万事大吉,因为还有一个更重要的环节就是AppStore审核,我们因为没有用到网络电话,缺使用了VoIP,所以上线被拒,而且项目中也不需要网络电话的功能,如果有知道怎么解决这个问题的小伙伴,那赶紧评论砸人吧,
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
<string>voip</string>
</array>
有小伙伴在别的文章中看到的如下图的设置,本质上和在info.plist里添加字段是一样的,只不过更加方便,大家根据自己的喜好就好了