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

[toc]

背景介绍

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

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

在短暂的心理活动后,秉着“客户第一,产品🐂🍺”的原则,于是回复说:“这功能太少见了,我先在网上看看吧,要是有其他 APP 有类似的功能,麻烦跟我说我参考一下。”然后,就祭出了程序员利器--Google,输入“iOS 后台 摇一摇”,只搜索出来的一个思路:<u>利用 CoreMotion 框架,监听加速计原始数据,然后在 APP 退到后台后,可以实现监听摇一摇的效果。</u>然而,并没有完整的代码或 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 处于后台或锁屏时,此方法不会回调。故<u>初步判定</u>此方法不能满足需求。

其他方法能否实现

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

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

利用 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 同时实现时,可能会出现效果上的相互干扰。不过,如果合理利用此功能,却可以为特殊用户群体提供极大的便利

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

偷偷的告诉大家,写到这里时,产品经理还没告诉我他所知道的哪个 APP 实现了这个功能,可能他太忙,给忘记了吧......

参考文章

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

Demo 链接

OCDailyTests/BackgroundShakeTest

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

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265