• 不会吧,这也行?iOS后台锁屏监听摇一摇


    背景介绍

    一般情况下,出于省电、权限、合理性等因素考虑,给人的感觉是很多奇怪的需求安卓可以实现,但是iOS就无法实现!今天要介绍的需求也有这种感觉,就是“当 APP 处于后台或锁屏状态时,依旧可以监听到摇一摇,进而触发某些功能,比如:语音播报”。

    在产品经理提出此需求的一瞬间,仿佛周边的空气都凝固了,我也犹如五雷轰顶,愣在原地。不由心想:“苹果爸爸怎么可能允许开发者实现这种功能!这得多费电啊!要是所有 APP 都这么做了,那还了得!” 与此同时,之前网上疯传、远近闻名的的需求--“做一个会根据手机壳颜色而改变主题颜色的APP”,清晰地浮现在脑海中,顿时一万只乌鸦从心中飘过。此时,产品经理解释到,这是咱们好多视力障碍用户提的需求,他们经常锁屏或把 APP 退到后台,且因为视力不佳原因,导致重新找到 APP 并切到前台的操作很是麻烦,所以十分希望我们能实现这个功能。原来如此。

    在短暂的心理活动后,秉着“客户第一”的原则,于是回复说:“这功能太少见了,我先在网上看看吧,要是有其他 APP 有类似的功能,麻烦跟我说我参考一下。”然后,就祭出了程序员利器--Google,输入“iOS 后台 摇一摇”,只搜索出来的一个思路:利用 CoreMotion 框架,监听加速计原始数据,然后在 APP 退到后台后,可以实现监听摇一摇的效果。然而,并没有完整的代码或 demo 。顿时,Talk is cheap, show me the code!这句经典台词突然地出现在脑海中!也看到有人评论说 CoreMotion 的确可以实现跟系统摇一摇类似的效果,但是退到后台或锁屏后,没办法监听到摇一摇事件。

    看到这条评论时,我不禁开始怀疑此功能是否真的可以被实现。

    玩归玩,闹归闹,开始 code,不开玩笑。

    接下来,开始自己的探索之旅。

    本文 demo 链接为 OCDailyTests/BackgroundShakeTest,可自行下载,方便运行和验证。

    探索过程

    其他 APP 有没有类似功能

    经过一番 Google,终于找到一款 APP 有类似功能::酷狗音乐 APP,对,就是那个在 PC 端一打开就会大喊 Hello KuGou!的音乐软件对应的 APP,万万没想到,手机 APP 也是这样,一句Hello KuGou!把我吓一跳。按如下步骤,在设置里打开此功能后,后台或锁屏时,摇一摇手机,可实现切歌的效果。

    既然的确有 APP 实现了此功能,那就踏踏实实地探索它可能是怎么实现的吧。

    系统提供的摇一摇回调能否满足

    系统摇一摇回调方法:

    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
        NSLog(@"%s", __FUNCTION__);
    }
    

    经测试,此方法只有在 APP 处于前台时,才会被回调。APP 处于后台或锁屏时,此方法不会回调。故初步判定此方法不能满足需求。

    其他方法能否实现

    此时,还是先根据网上各路大神提供的思路进行尝试,即利用 CoreMotion 框架,监听加速计原始数据,然后在 APP 退到后台后,实现监听摇一摇的效果。

    好,我们先利用 CoreMotion 框架,监听加速计原始数据,实现类似系统摇一摇回调的效果。

    利用 CoreMotion 框架,监听加速计原始数据

    通过加速计监听摇一摇

    因加速计回调比较频繁,因此比较占用资源,故把此功能设计为单例。

    • 快速实现单例效果

      //具体实现详见 demo 中文件
      #import "HMSingleton.h"
      
      @interface MYAccelerometerTool : NSObject
      HMSingleton_h(MYAccelerometerTool);
      @end
      
      @implementation MYAccelerometerTool
      HMSingleton_m(MYAccelerometerTool);
      @end
      
    • 声明和懒加载运动管理员属性

      @property(nonatomic, strong) CMMotionManager *gMotionMnger;
      
      - (CMMotionManager *)gMotionMnger{
          if (nil == _gMotionMnger) {
              CMMotionManager *lMnger = [[CMMotionManager alloc] init];
              lMnger.accelerometerUpdateInterval = 0.1;
              [lMnger startAccelerometerUpdates];
              _gMotionMnger = lMnger;
          }
          return _gMotionMnger;
      }
      
    • 声明和实现时间戳属性,用于实现节流效果(为防止频繁回调,每次检测成功后,停止摇动 1s 后才继续响应下次摇一摇。)

      @property(nonatomic, strong) NSDate *gDateLastShakeSuc;
      - (NSDate *)gDateLastShakeSuc{
          if (nil == _gDateLastShakeSuc) {
              _gDateLastShakeSuc = [NSDate distantPast];
          }
          return _gDateLastShakeSuc;
      }
      
    • 开始监听摇一摇动作

      - (BOOL)startMonitorShake{
          if (NO == self.gMotionMnger.isAccelerometerAvailable) {
              return NO;
          }
          
          //监听中,直接返回YES
          if (self.gMotionMnger.isAccelerometerActive) {
              return YES;
          }
          
          [self.gMotionMnger startAccelerometerUpdatesToQueue:[NSOperationQueue new] withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
              
              CMAcceleration acceleration = accelerometerData.acceleration;
              
              //综合x、y两个方向的加速度(z方向速度无意义,用的话,走路上下抖手机时会误触发,系统摇一摇也不会被z轴加速度触发)
              //当综合加速度大于2.3时,就激活效果(数据越小,用户摇动的动作就越小,越容易激活)
              double accelerameter = sqrt( pow( acceleration.x , 2 ) + pow( acceleration.y , 2 ));
              
              if (accelerameter > 2.3) {
                  
                  //节流效果:距离上次摇一摇成功事件,间隔时间小于1s时,认为无效
                  NSDate *lCrtDate = [NSDate date];
                  if ([lCrtDate timeIntervalSinceDate:self.gDateLastShakeSuc] < 1) {
                      self.gDateLastShakeSuc = lCrtDate;
                      return ;
                  }
                  
                  self.gDateLastShakeSuc = lCrtDate;
                  [[NSNotificationCenter defaultCenter] postNotificationName:KNTFY_SHAKE_SUCCESS object:nil];
              }
          }];
          
          return YES;
      }
      
    • 为了代码的对称美和可能的相关业务,实现停止监听摇一摇方法

      - (void)stopMonitorShake{
          [self.gMotionMnger stopAccelerometerUpdates];
          self.gMotionMnger = nil;
          self.gDateLastShakeSuc = nil;
      }
      

    控制器相关逻辑和代码

    • 开始监听摇一摇

       BOOL lRes = [[MYAccelerometerTool sharedMYAccelerometerTool] startMonitorShake];
       NSLog(@"lRes:%d", lRes);
       NSAssert(lRes, @"开始监听摇一摇失败");
      
    • 监听摇一摇成功的通知

       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nmShakeSuccess:) name:KNTFY_SHAKE_SUCCESS object:nil];
      
      //在摇一摇的同时,通过观察此方法是否有log,可以判断是否有监听到。
      - (void)nmShakeSuccess:(NSNotification *)ntfy{
          NSLog(@"%s", __FUNCTION__);
      }
      
    • dealloc方法中取消监听

      - (void)dealloc{
          [[NSNotificationCenter defaultCenter] removeObserver:self];
      }
      

      运行 demo 工程,测试可知,通过上述方法,的确可以在 APP 处于前台时,实现监听摇一摇动作的效果。可是,当把 APP 退到后台或锁屏时,nmShakeSuccess 方法不再有 log,即:APP 处于后台时,通过监听加速计的方法,默认也无法在 APP 处于后台或锁屏时实现监听效果。这也印证了上文提到的那个评论者的疑问。

      可是 Hello KuGou! 明明实现了后台或锁屏时摇一摇的效果啊!难道是需要额外的配置?联想 iOS 处于后台时,默认会把 APP 的服务给挂起(suspended),只有当 APP 通过某种方式(后台定位/播放音乐/蓝牙扫描等)具有后台运行权限时,才可以一直保活。可猜想,也许赋予 APP 具有后台运行的权限后,就可以实现想要的功能了。于是,开始进行验证如下。

    APP 申请后台运行权限后,能否监听到摇一摇

    因为工作中很多 APP 具有后台定位权限和相关功能,所以本文通过为 APP 申请后台定位权限来验证。

    APP 申请后台定位权限

    • plist 文件中增加”定位请求描述信息“

      <key>NSLocationAlwaysUsageDescription</key>
      	<string>我们需要根据您的定位提供周边搜索和导航服务</string>
      	<key>NSLocationWhenInUseUsageDescription</key>
      	<string>我们需要根据您的定位提供周边搜索和导航服务</string>
      

      增加”后台定位权限“

      <key>UIBackgroundModes</key>
      	<array>
      		<string>location</string>
      	</array>
      
    • 声明定位管理员属性

      @property(nonatomic, strong) CLLocationManager *gMnger;
      
    • 懒加载定位管理员,请求定位权限、允许后台位置更新

      - (CLLocationManager *)gMnger{
          if (nil == _gMnger) {
              _gMnger = [[CLLocationManager alloc] init];
              _gMnger.delegate = self;
              _gMnger.allowsBackgroundLocationUpdates = YES;
              [_gMnger requestWhenInUseAuthorization];
          }
          return _gMnger;
      }
      
    • 代理 3 步走(用于验证后台定位是否生效)

      • 遵守代理协议

        @interface ViewController ()<CLLocationManagerDelegate>

      • 指定代理对象

        _gMnger.delegate = self;

      • 实现代理方法

        #pragma mark -  delegate
        - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{
            NSLog(@"%s", __FUNCTION__);
        }
        
        - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
            NSLog(@"%s", __FUNCTION__);
        }
        
    • APP 后台或锁屏后,测试能否成功监听摇一摇

      运行 demo 工程,经测试,把 APP 退到后台或锁屏,或即退到后台又锁屏,都能够检测到摇一摇事件。

    多 APP 都实现此功能时,摇一摇是何效果

    这里用 demo APP 和酷狗音乐 APP 进行测试。

    • 同时打开这两个 APP,其中酷狗音乐 APP 打开后台摇一摇切歌的功能。
    • 酷狗音乐 APP 开始放歌,退到后台。
    • demo APP 打开后,退到后台。
    • 摇一摇,查看效果:
      • 当摇动的力度不是很大时,demo APP 回调方法会被触发;
      • 当摇动的力度很大时,demo APP 回调方法和酷狗 APP 切歌会同时被触发;
    • 由此可见,如果多个 APP 同时实现了此功能时,那么后台或锁屏摇一摇时,只要满足了某个 APP 实现的加速计相关判定条件,就可以同时触发多个 APP 对应的效果。

    后台定位权限 + 系统摇一摇,是否可行?

    经测试,还是不行。果然,系统摇一摇还是比较受限的,只能在前台回调。

    文章小结

    想要实现”iOS后台锁屏监听摇一摇“功能,

    首次,必须满足一个硬性条件:APP 具有某种后台运行的权限。

    其次,技术实现上必须使用CoreMotion框架,通过监听加速计回调自己实现对摇一摇事件的监听判定

    最后,可通过增加时间属性,实现对摇一摇事件监听时的节流效果,防止持续摇动时,太过频繁的事件回调。

    此外,多 APP 都实现此功能时,摇一摇的效果是:只要摇动力度很大,加速计数据满足 APP 实现的摇一摇判定条件,就可以同时触发多个 APP 各自对应的效果

    因此,如果不是 APP 特别需要此功能,尽量不要这样实现,毕竟,比较占用系统资源,而且太多 APP 同时实现时,可能会出现效果上的相互干扰。不过,如果合理利用此功能,却可以为特殊用户群体提供极大的便利

    通过探索,满足了视力障碍用户的迫切需求,还是蛮有成就感的!

    参考文章

    iOS应用退出到后台后怎样监听摇晃事件

    Demo 链接

    OCDailyTests/BackgroundShakeTest

    最后,感谢“技术创作101训练营”!通过参加训练营,让我对写作有了更深入的认识和更高的心里觉悟。

    学无止境,快乐编码。 没有一种不经过蔑视、忍受和奋斗就可以征服的命运。
  • 相关阅读:
    微信二维码 场景二维码 用于推送事件,关注等 注册用户 ,经过测试
    简单的 Helper 封装 -- CookieHelper
    简单的 Helper 封装 -- SecurityHelper 安全助手:封装加密算法(MD5、SHA、HMAC、DES、RSA)
    Java反射机制
    Windows Azure Web Site (13) Azure Web Site备份
    Windows Azure Virtual Machine (1) IaaS用户手册
    Windows Azure Web Site (1) 用户手册
    Windows Azure Web Site (12) Azure Web Site配置文件
    Windows Azure Web Site (11) 使用源代码管理器管理Azure Web Site
    Windows Azure Web Site (10) Web Site测试环境
  • 原文地址:https://www.cnblogs.com/Dast1/p/13784034.html
Copyright © 2020-2023  润新知