1、前言

微信为了解决小商户老板们在频繁交易中不方便核对、确认到账的功能痛点,产品MM提出了新版本需要支持收款到账语音提醒功能。本文借此总结了iOS平台上的APP后台唤醒和语音合成、播放等一系列技术开发过程中遇到的坑和小技巧,希望与您分享。

2、技术方案

2.1 后台唤醒App

收款到账语音提醒需要收款方在收到款后,播放一段TTS合成语音播报金额,微信在前台时可以通过模板消息将需要播报的金额带下来,再请求TTS数据并播放,但是app在挂起或者被kill掉的情况下要如何请求语音数据并播放呢?

iOS提供了两种方式唤醒处于挂起或已经被kill掉的app。分别是Silent Notification和VoIP Push Notification,客户端在被唤醒之后将获得30s的后台运行时间,这段运行时间足以请求合成语音数据并播放。

具体技术细节如下:

1)Silent Notification:Silent Notification在iOS7以上便可以支持,但是每小时能推送的Silent Notification次数有限制;

2)VoIP Push Notification:VoIP Push Notification则是在iOS8以上才支持的新Push类型,相比于Silent Notification,VoIP Push具有高优先级、低延迟的优势,并且没有次数限制。

对比这两种技术方案,VoIP Push Notification明显更适合用于收款到账语音提醒的唤醒方案。

2.2 TTS合成语音

TTS语音合成方案分为离线合成方案和在线合成方案,离线合成方案省去网络请求,合成速度更快,节省网络流量,但是合成音的听起来比较机械,语速和停顿的处理较差一些。如果对合成音的效果要求不是特别高,可以考虑采用iOS自带的AVSpeechSynthesis框架,免去语音库的合入,减少安装包大小。

在线合成方案的效果则相对更像人声,富有感情。考虑到产品体验,我们采用了搜索产品部提供的在线语音合成方案,接入方式可以看这篇文章,合成音格式支持wav、mp3、silk,amr、speex。对比后发现,在合成相同文本的情况下,amr的压缩率最高,但是能听到音质下降明显。silk格式压缩率次高,且能保持相对清晰的音质,单条合成语音大小在2KB左右。

2.3 唤醒后播放音频文件

在请求到合成语音后,要在后台或者锁屏状态下播放音频文件,AVAudio Session的Category值需要使用AVAudioSessionCategoryPlayback或是AVAudioSessionCategoryPlayAndRecord,CategoryOptions根据实际需要可选择MixWithOthers(与其他声音混音)或是DuckOthers(调低其他声音的音量)。

需要注意的是:只有iOS10以上才支持app被唤醒后在后台/锁屏状态下播放音频。所以iOS10以下的设备,在收到VoIP Push后只能在local push上设定一段固定铃声,这也是为什么iOS10以下只有“微信支付收款到账”,而没有后面具体的金额数值。

3、静音开关检测

不幸的是,在产品发布后没多久就受到了某互联网大佬的吐槽。

从产品体验上来说,收款到账的金额播报是随着local push的弹出一起播放的,更像是一种特殊的push铃声,而苹果对push铃声的处理是受到静音开关控制的,所以讲道理,这个吐槽是合理的。然而前面提到App在被VoIP Push唤醒之后,需要将AudioSessionCategory设置为AVAudioSessionCategoryPlayback或AVAudioSessionCategoryPlayAndRecord才可以在后台播放音频文件,这两种模式是不受静音开关控制的。要实现这个需求,就必须获取当前静音开关的状态。而苹果在iOS5之后并没有明确地提供一种方式让开发获取静音开关的状态,这就陷入了一个尴尬的局面。

苹果在iOS5之前可以使用以下方式监听静音键开关:

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)isMuted 
    CFStringRef route; 
    UInt32 routeSize = sizeof(CFStringRef); 
    OSStatus status = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routeSize, &route); 
    if(status == kAudioSessionNoError) 
    
        if(route == NULL|| !CFStringGetLength(route)) 
            returnYES; 
    
    returnNO; 
}

苹果在iOS5之后便禁止了使用这种方式监听静音按键,背后的原因应该是苹果希望开发者使用AVAudioSession来提供统一的音频播放效果。

最后我在Reddit上找到了一种曲线救国的方式,实现起来也不复杂:使用AudioServicesPlaySystemSound播放一段0.2s的空白音频,并监听音频播放完成事件,如果从开始播放到回调完成方法的间隔时间小于0.1s,则意味当前静音开关为开启状态。

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
void SoundMuteNotificationCompletionProc(SystemSoundID  ssID,void* clientData){
    MMSoundSwitchDetector* detecotr = (__bridge MMSoundSwitchDetector*)clientData;
    [detecotr complete];
}
- (instancetype)init {
    self= [superinit];  
     if(self) {
        NSURL*pathURL = [[NSBundlemainBundle] URLForResource:@"mute"withExtension:@"caf"];    
        if(AudioServicesCreateSystemSoundID((__bridge CFURLRef)pathURL, &_soundId) == kAudioServicesNoError){
            AudioServicesAddSystemSoundCompletion(self.soundId, CFRunLoopGetMain(), kCFRunLoopDefaultMode, SoundMuteNotificationCompletionProc,(__bridge void*)(self));
            UInt32 yes = 1;
            AudioServicesSetProperty(kAudioServicesPropertyIsUISound, sizeof(_soundId),&_soundId,sizeof(yes), &yes);
        else{
            MMErrorWithModule(LOGMODULE, @"Create Sound Error.");
            _soundId = 0;
        }
    }    returnself;
}
- (void)checkSoundSwitchStatus:(CheckSwitchStatusCompleteBlk)completHandler {  
         if(self.soundId == 0) {
        completHandler(YES);     
        return;
    }
    self.completeHandler = completHandler;
    self.beginTime = CACurrentMediaTime();
    AudioServicesPlaySystemSound(self.soundId);
}
- (void)complete {
    CFTimeInterval elapsed = CACurrentMediaTime() - self.beginTime;
    BOOLisSwitchOn = elapsed > 0.1;  
     if(self.completeHandler) {
        self.completeHandler(isSwitchOn);
    }
}

4、设置声音阈值

另外一个用户反馈较多的问题是听不到播报声音,通过查看日志发现是触发语音播报时,用户设置的系统音量过小所导致。首先想到的解决方案是直接设置AVAudioPlayer的volume(或者是AudioQueue中的kAudioQueueParam_Volume),然而实验过后发现这样行不通,volume属性受制于系统音量(比如系统volume是0.5,AVAudioPlayer的音量是0.6,则最终的音量为0.5*0.6 =0.3)。

要解决音量过小的问题,还是需要通过调节系统音量。最终的解决方案借鉴了进入收付款展示二维码时自动调节屏幕亮度的方案:如果屏幕亮度未达到阈值,则调高屏幕亮度到阈值,离开页面时,将亮度设回原亮度。同理,播放提示音时,若用户设置的系统音量小于阈值,则调节到阈值。提示音播放完毕后,将提示音调回原音量。

控制系统音量有以下两种方式。

4.1 方式一:通过MPMusicPlayerController设置音量

1
2
//This property is deprecated -- use MPVolumeView for volume control instead.mpc.volume = 0;  //0.0~1.0
MPMusicPlayerController *mpc = [MPMusicPlayerController applicationMusicPlayer];

第一种方式简单粗暴,在设置的时候会弹出系统音量提示框,如果用户在使用app的过程突然弹出音量框,会对用户造成困扰,不建议使用这种方式,并且苹果在iOS7.0以后已将该属性标为deprecated。

4.2 方式二:通过MPVolumeView设置音量

第二种方式则是将一个看不见的MPVolumeView添加到当前视图上,系统音量提示框就不会显示了。

需要注意的是:在调节完系统音量需要将MPVolumeView移除,否则后续用户手动调节音量会出现系统音量提示框不显示的情况。

调节音量的方式,则是先取到MPVolumeView中名为MPVolumeSlider的子View,并对其发送模拟用户操作的事件。

1
2
3
4
5
6
7
8
9
10
11
12
- (void)setSystemVolume:(float)volume {
    UISlider* volumeViewSlider = nil; 
   for(UIView *view in [self.m_privateVoulmeView subviews]){    
      if([view.class.description isEqualToString:@"MPVolumeSlider"]){
            volumeViewSlider = (UISlider*)view;       
               break;
        }
    }    if(volumeViewSlider != nil) {
        [volumeViewSlider setValue:volume animated:NO];        //通过send
        [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
    }
}

转载于:https://www.cnblogs.com/xiyangxixia/p/8465769.html

iOS后台唤醒实战:微信收款到账语音提醒技术总结相关推荐

  1. Golang 实战——微信公众号课程提醒系统

    Golang 实战--微信公众号课程提醒系统 起因 最早开始学 Golang 已经是整整一年前了,当时就把基础语法那一块学完了,然后拿 Golang 写了点 leetcode 题.之后由于项目里一直用 ...

  2. ios 后台唤醒应用_苹果不用背锅了!微信被杀后台是因为“耍流氓”?用户该听谁的?...

    文 | 考拉科技馆 排版 | 考拉科技馆 原创文章,禁止转载,违者必究! 苹果表示不用背锅了.日前,苹果推送了最新的iOS 13.2系统,在升级到iOS 13.2系统后,很多果粉都在网上反映iOS 1 ...

  3. ios 后台唤醒应用_IOS开发之----详解在IOS后台执行

    文一 我从苹果文档中得知,一般的应用在进入后台的时候可以获取一定时间来运行相关任务,也就是说可以在后台运行一小段时间. 还有三种类型的可以运行在后以, 1.音乐 2.location 3.voip 文 ...

  4. ios 后台唤醒应用_iOS 后台运行机制

    很多使用iphone的孩纸,可能并不了解iphone的后台运行机制.今天转载一篇网友的文章,来扫盲一下. 标注来源:http://blog.csdn.net/enuola/article/detail ...

  5. ios 后台唤醒应用_手机应用后台不断唤醒,耗电大,荣耀手机只需简单几步就可以解决...

    使用安卓手机都有一个痛点:安装的应用不停的唤醒,在后台一直驻留,甚至还间接的唤醒其他应用,可想而知手机的不正常耗电也非常大.这里介绍一下华为荣耀手机专门解决后台应用问题的设置. 这里要介绍的就是华为荣 ...

  6. 微信团队原创分享:iOS版微信的内存监控系统技术实践

    为什么80%的码农都做不了架构师?>>>    本文来自微信开发团队yangyang的技术分享. 一.前言 FOOM(Foreground Out Of Memory),是指App在 ...

  7. 腾讯技术分享:微信小程序音视频技术背后的故事

    1.引言 微信小程序自2017年1月9日正式对外公布以来,越来越受到关注和重视,小程序上的各种技术体验也越来越丰富.而音视频作为高速移动网络时代下增长最快的应用形式之一,在微信小程序中也当然不能错过. ...

  8. 微信小程序ios端唤醒不了拨打电话或者部分电话拨打不了解决方案

    目录 简介 技术 问题 解决方案 简介 主要解决微信小程序ios端唤醒不了拨打电话或者部分电话拨打不了问题,使用安卓手机就可以轻松的唤醒拨打电话. 技术 uni-app前端小程序框架 问题 在调试微信 ...

  9. 微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)

    http://www.52im.net/thread-210-1-1.html 哪些部分需要"保活"? 按照我们的理解包含两部分: 网络连接保活: 如何保证消息接收实时性.详见本文 ...

  10. iOS 后台运行实现总结

    原文:http://www.jianshu.com/p/d3e279de2e32 iOS 后台运行的规则 应用的运行状态分为以下五种: Not running:应用还没有启动,或者应用正在运行但是途中 ...

最新文章

  1. 10只机器狗拉卡车!井然有序,毫不费力 | 极客头条
  2. 一次I/O问题引发的P0重大故障[改版重推]
  3. android studio 抓log,Android studio保存logcat日志到本地的操作
  4. Redis通信协议(protocol)
  5. 【javascript】数据结构-链表
  6. 基于指数平滑对心电信号进行PQRST模拟(第二种方案)
  7. 两个oracle数据库外网同步,利用DBLink+JOB实现两个Oracle数据库之间的数据同步
  8. 【网站推荐】Solaris 平台编写设备驱动程序
  9. javascript java html_JS入门篇(二):在html中如何使用Javascript
  10. 单片机点亮一个灯程序_初识单片机C语言编程,点亮第一个LED灯
  11. 自学python能找到工作吗-自学Python如何找工作?多久能找到工作?
  12. idea中从外部复制内容到里面不成功
  13. VS代码格式化快捷键
  14. 清华大学计算机系高考选科要求,清华大学新高考选课要求-清华大学新高考选考科目...
  15. 裤子尺码对照表eur40_裤子尺寸对照表,衣服尺寸对照表,服装尺寸对照表- 尺码对照表...
  16. Python—猫眼电影票房爬虫实战 轻松弄懂字体反爬!
  17. 消息队列,问题与处理方案梳理
  18. 从事IT业一个8年老兵转行前的自我总结1——初爻
  19. 6. 文本分类——transformer模型
  20. Nginx下的反向代理 双层代理 负载均衡

热门文章

  1. 儒豹手机搜索发布2008年各频道关键词排行榜
  2. 易语言利用服务器更新,【原创】利用FTP实现软件自动更新
  3. oracle双机热备 rose,linux下使用RoseHa实现oracle双机热备.doc
  4. 智慧城市的投资运营与评估
  5. unity3D人物动画
  6. 【Spring】概述及IOC理论推导
  7. 鲁棒控制器设计方法(systune,hinfsyn,musyn...)
  8. npp php,Notepad++ Home下载,NPP官方下载
  9. noip2016题解
  10. 【硬件驱动系列】DirectSound vs ASIO