听说可以通过iBeacon激活IOS App,便研究了一下。
一、基本原理
iBeacon一般用于外设的,外设加入了iBeacon后,就会不断广播一定范围的信号。如果App加入了iBeacon的监听,那么如果App进入了外设的广播范围,那么App里面的iBeacon回调就会有反应,也是通过这种方法激活被挂起的App。因为我没有嵌入了iBeacon的外设,这里我是通过把mac改造成iBeacon外设,网上有个mac平台的软件:
https://github.com/timd/MactsAsBeacon
这软件界面如下:
然后,看到这个界面你知道App怎么初始化iBeacon了吧:
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:IBEACON_UUID];
_beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:2 minor:1000 identifier:[[NSBundle mainBundle] bundleIdentifier]];
二、IOS端IBeacon的开发
iBeacon的使用是基于蓝牙和定位的,所以我这里使用了权限:(网上都说是需要基于蓝牙的,但我测试过程中,把蓝牙关闭了,也是可以的,但我还是加入了蓝牙的权限)
Location Always and When In Use Usage Description
Bluetooth Peripheral Usage Description
bluetooth-peripheral
location
App的代码部分:
@property(nonatomic) CLLocationManager *locationManager;
@property(nonatomic) CLBeaconRegion *beaconRegion;
- (CLLocationManager *)locationManager{
if(!_locationManager){
_locationManager = [[CLLocationManager alloc] init];
_locationManager.distanceFilter = 1.0f; //kCLDistanceFilterNone
_locationManager.delegate = self;
//控制定位精度,越高耗电量越
// _locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
}
return _locationManager;
}
- (CLBeaconRegion *)beaconRegion{
if(!_beaconRegion){
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:IBEACON_UUID];
// _beaconRegion = [[CLBeaconRegion alloc]initWithProximityUUID:uuid identifier: [[NSBundle mainBundle] bundleIdentifier]];
_beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:2 minor:1000 identifier:[[NSBundle mainBundle] bundleIdentifier]];
_beaconRegion.notifyOnExit = YES;
_beaconRegion.notifyOnEntry = YES;
_beaconRegion.notifyEntryStateOnDisplay = YES;
}
return _beaconRegion;
}
//申请定位权限并且开启iBeacon监听
- (void)startLocation{
CLAuthorizationStatus state = [CLLocationManager authorizationStatus];
if(state == kCLAuthorizationStatusNotDetermined){
if([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]){
[self.locationManager requestAlwaysAuthorization];
}
}
if(state == kCLAuthorizationStatusDenied){
NSString *message = @"您的手机目前未开启定位服务,如欲开启定位服务,请至设定开启定位服务功能";
UIAlertController * ac = [UIAlertController alertControllerWithTitle:@"Tip" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
;
}];
[ac addAction:action1];
[self presentViewController:ac animated:YES completion:nil];
return;
}
if(state == kCLAuthorizationStatusRestricted){
NSString *message = @"定位权限被限制";
UIAlertController * ac = [UIAlertController alertControllerWithTitle:@"Tip" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
;
}];
[ac addAction:action1];
[self presentViewController:ac animated:YES completion:nil];
return;
}
if( [CLLocationManager isMonitoringAvailableForClass:[self.beaconRegion class]] ) {
//开始定位
[self.locationManager startMonitoringForRegion:self.beaconRegion];
}
if (_beaconRegion && [CLLocationManager isRangingAvailable]) {
NSLog(@"startRangingBeaconsInRegion");
//开启监听
[_locationManager startRangingBeaconsInRegion:_beaconRegion];
}
}
iBeacon的各种回调:
// delegate
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region{
_errorLab.text = @"didStartMonitoringForRegion";
WLlog(@"didStartMonitoringForRegion");
}
// 设备进入该区域时的回调
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
//App在后台才会执行
self.view.backgroundColor = [UIColor redColor];
_errorLab.text = @"didEnterRegion";
WLlog(@"didEnterRegion");
}
// 设备退出该区域时的回调
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
//App在后台才会执行
self.view.backgroundColor = [UIColor blackColor];
_errorLab.text = @"didExitRegion";
WLlog(@"didExitRegion");
}
// 有错误产生时的回调
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(nullable CLRegion *)region withError:(NSError *)error{
_errorLab.text = @"monitoringDidFailForRegion";
}
- (void)locationManager:(CLLocationManager *)manager
didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
NSString *proximity = @"unKnow";
CLBeacon *beacon = [beacons firstObject];
switch (beacon.proximity) {
case CLProximityImmediate:
{
// NSLog(@"very close");
proximity = @"very close";
}
break;
case CLProximityNear:
{
// NSLog(@"near");
proximity = @"near";
}
break;
case CLProximityFar:
{
// NSLog(@"far");
proximity = @"far";
}
break;
default:
{
NSLog(@"unKnow");
}
break;
}
proximity = [NSString stringWithFormat:@"%@\n%f meter\nrssi:%zi",proximity,beacon.accuracy,beacon.rssi];
_textLab.text = proximity;
// NSLog(@">>>>%f meter rssi:%zi",beacon.accuracy,beacon.rssi);
}
- (void)locationManager:(CLLocationManager *)manager
didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region API_AVAILABLE(ios(7.0), macos(10.10)) API_UNAVAILABLE(watchos, tvos){
//第一次运行的时候,会走这里,之后App在前台的情况下,这里就算状态发送改变了,也没有执行。App在后台才会执行
NSString *msg = @"didDetermineState";
if(state == CLRegionStateInside){
msg = @"Inside";
[[LocalNotifyManager sharedInstanced] pushLocalNotification:@"Tip" alertBody:msg flag:@"test" infoDic:nil];
[self checkAppLiveTime];
}
if(state == CLRegionStateOutside){
msg = @"Outside";
[[LocalNotifyManager sharedInstanced] pushLocalNotification:@"Tip" alertBody:msg flag:@"test" infoDic:nil];
[self checkAppLiveTime];
}
_errorLab.text = msg;
WLlog(msg);
}
很多人说didEnterRegion和didExitRegion都没有执行,我测试过了,只有App进入到后台时候,这两个方法才有机会执行。
三、App后台被激活的时间
我使用了通知来测试后台的激活。App进入到后台了,通知被激活了,说明App确实被激活了。
我接着测试了App被激活的时间:
- (void)checkAppLiveTime{
//经过测试,每次App可有10秒左右的激活时间,加上starBGTask后,可以存活几分钟
// [self starBGTask];
if(_liveTimer){
[_liveTimer invalidate];
_liveTimer = nil;
}
_liveTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeAction) userInfo:nil repeats:YES];
}
通过定时器,每秒写入内容到文件中。测试的结论是,激活时间大概10秒左右。当然可以通过设置后台任务让时间延长一点,代码如下:
- (void)starBGTask {
if (_bgTask == UIBackgroundTaskInvalid) {
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"EndBackgroundTask");
bgTask = UIBackgroundTaskInvalid;
}];
_bgTask = bgTask;
}
}