微信JSSDK与录音相关的坑

最近一直在做微信JSSDK与录音相关的功能开发, 遇到了各种奇尺大坑, 时不时冷不丁地被坑一道, 让我时常想嘶吼: "微信JSSDK就是个大腊鸡!!!!!!!!!!"

现在工作得到阶段性成果, 有时间休息总结下, 故来整理一下这段时间碰到的bug, 希望做个前车之鉴, 劝大家谨慎入坑.

checkJsApi

功能: 判断当前客户端版本是否支持指定JS接口

转载:  http://www.fwqtg.net/%E5%BE%AE%E4%BF%A1jssdk%E4%B8%8E%E5%BD%95%E9%9F%B3%E7%9B%B8%E5%85%B3%E7%9A%84%E5%9D%91.html

我遇到的一个不大不小的坑是: onVoiceRecordEndonVoicePlayEnd总是返回false, 即使这两个接口是被支持的!

还有要注意的是, 这个API只是检查当前客户端版本是否支持该API, 与该API是否开启并没有卵关系! 所以当你check某个API发现是true, 但是又怎么调用都不起作用的时候, 记得检查下wx.config中的jsApiList参数.

startRecord, stopRecord

微信JSSDK的API都有一个大问题, 就是如果调用时间间隔过短, 就非常可能产生无效调用. 无效调用的意思是, 虽然你调用了某个API, 但是相当于没调用, 它不会触发successfailcomplete中的任何一个callback!

打比方说, 你做一个"按下录音, 抬手停止录音"的功能, 如果用户点击了一下录音按钮, 相当于快速地startRecord然后stopRecord, 那么stopRecord是极有可能是无效的, 不会执行任何callback.

然而问题还不止这个, 微信JSSDK的调用是异步的. 举例来说就是, 你调用startRecord的时间, 和startRecordsuccess的callback被执行的时间可能间隔了若干毫秒甚至秒. 这意味着, 用户点击按钮可能会造成: 虽然是先调用startRecord再调用stopRecord, 但是可能stopRecord先于startRecord调用成功!

我做了各种尝试后意识到, 微信JSSDK太脆弱, 频繁操作就会被玩儿坏. 所以我最终的结论是: ==一定要在微信JSSDK外面包一层, 控制API的调用频率.==

以录音为例, (我曾经的解决方案见这里, 但是后来发现频繁地调用stopRecord还是会有问题) 我现在的解决方案是: 当用户按下录音时调用startRecord, 然后一秒之内抬手都会提示用户"录音太短", 然后在第一秒结束时再调用stopRecord, 这样可以确保两个API之间的调用间隔至少一秒, 不会崩掉.

最靠谱的方法还是用状态机来做, 我目前的状态机如下图. 图中没有包含uploadVoiceuploadVoice期间应该不允许用户操作按钮.

WinPhone上无法录音

通过WinPhone访问微信官方测试页面http://demo.open.weixin.qq.com/jssdk尝试调用录音接口, 然而并没有卵用!!!! (WinPhone用户再次受到一万点伤害)

好, 接下来只讨论iPhone和Android用户…

iPhone中的假录音状态

机型: iPhone

症状: 微信显示"录音中", 但是其实没有在录音! 此时, startRecord会失败, 错误信息startRecord:fail;stopRecord不调用任何callback, 仍然显示录音中. 那就这么挂了? 不…

触发方式:

  1. 录音中按Home返回桌面
  2. 录音中从屏幕底部上划打开iPhone的设置菜单, 再关闭菜单.

解法: 此时你需要先调用stopRecord, 虽然不会触发任何callback, 但微信内部一定设置了某个flag, 然后就可以正常调用startRecord了.

呵呵… 一口鲜血喷向屏幕.

你无法知道是否进入了假录音状态, 所以这个问题暂时无解!

startRecord, 直接stopRecord有什么效果?

我的测试结果是, 三个callback都不会被触发!

我觉得这应该算是一个bug, 如果没有在录音但是调用了stopRecord, 那应该失败并且errMsgNotRecording.

录音中按Home返回桌面

iPhone: 录音中断, 回到微信进入假录音状态.

Android: 录音继续, 回到微信可以正常stopRecord.

录音中打开设置菜单

iPhone: 从屏幕底部上滑可以打开设置菜单, 关闭菜单后进入假录音状态.

Android: 从屏幕上部下滑可以打开设置菜单, 关闭菜单后可以正常停止录音.

录音中关闭WebApp

iPhone: 录音结束, 回到WebApp可以正常开启录音.

Android: 录音继续, 回到WebApp调用startRecord失败提示recording.

录音中刷新WebApp

iPhone: 微信不再显示"录音中", 但是实际上此时在录音… startRecord失败, 错误信息startRecord:fail;stopRecord会成功.

Android: 我没有刷新按钮… (Android用户起立鼓掌)

iPhone上录音后播放audio声音变得特别小

这是一个微信JSSDK炒鸡恶心的BUG… 只在iPhone出现: 在你使用的是外放(就是声音是从手机下方的小音箱里面放出来的)的前提下, 录音之后, 播放audio/video声音会转而从听筒(就是不插耳机打电话是耳朵对着的位置)播放出来. 如果你不知道的话, 会以为录音之后播放audio/video声音变得特别小.

但是, 如果你用耳机的话则不会受到影响, 因为声音会始终从耳机里播放出来. (难道你要我告诉用户, "请带上耳机使用本产品"嘛?!)

后来我们发现playVoice一次之后, audio/video的声音就会, 神奇地, 又从外放里播放出来了… 无语凝噎.jpg

所以, 有一个丑, 但是管用的workaround… 就是, 每次stopRecord成功后之后立即调用playVoicestopVoice… 这样至少听筒模式的bug解了.

但是还有个问题是, 如果你播放的是用户刚刚录的音, 有可能播放录音时会发出"噗"的一声… 这是录音时录进去的杂音. 想要解决这个问题你就必须要上传一个空白的语音到微信, 然后在WebApp启动的时候通过serverId下载这个语音, 然后每次stopRecord的时候播放这个空白语音…

然后还有个问题就是, 你上传的这个语音必须是临时素材, 这样下载下来才会是localId, 可以用playVoice播放; 如果你上传语音作为用旧素材的话, 下载下来是二进制, 不能用playVoice播放…

然后还有个问题就是, 临时素材只会被微信保留三天… 三天后过期… 所以你必须每三天上传一段新的空白语音, 更新serverId…

抱头痛哭.jpg

Android上无法实现长按录音的功能?

经过测试发现, (很多?)Android上无法实现长按录音, 为什么?

因为我发现, 只要调用了startRecord就会触发按钮的touchcancel事件. 于是, 之后的touch相关事件就不会再被监听了. (本人猜测, 这和下面的uploadVoice导致UI卡住是一个问题, 在Android上微信JSSDK的执行会卡住UI导致触发了按键的touchcancel事件)

这意味着即使你手还按在按键上, 浏览器已经认为你松手了; 等你真的松手的时候, 浏览器并没有在监听touchend事件了…

所以目前, 虽然是一套代码运行在iPhone和Android上, 但是iPhone上就是长按录音, 但是Android上需要用户点击开始(响应touchstart), 点击结束(响应touchend)…

事实上, 两次点击录音比长按录音更容易出bug, 因为第一次和第二次点击之间用户可以随便乱操作, 很容易出现各种问题; 但是长按期间, 用户乱操作的可能性会低得多.

(突然想到, 可不可以在Android系统上, 当调用startRecord后触发touchcancel时, 手动触发一下按键的touchstart事件, 这样让按钮能够监听touchend? 之后做做实验)

初次以及未知时间间隔之后, 提示用户"是否开启录音"

新用户进入WebApp后第一次调用startRecord的时候, 微信会弹出一个对话框, 询问用户"是否开启录音".

这对于"长按录音"操作来说, 非常影响体验, 因为用户按到一半需要松手去点对话框.

如果用户一不小心点了"否", 那你可以去哭了, 接下的录音API调用会一直失败.

如果用户点了"是", 接下来, 至少一段时间内, 用户可以安心地录音了, 不会弹出对话框.

但是, 恶心的是, 在不确定的(至少目前我还没找到规律)时间之后(比如4天之后)用户再次打开WebApp时微信可能会重新询问"是否开启录音"…

有一个不完美的解决方法是: 在刚刚打开WebApp的时候就尝试录音一下. 如果微信弹出对话框, 用户可以在这个时候点选是/否, 而不至于影响真正录音时的体验.

吐槽onVoiceRecordEndonVoicePlayEnd

先说onVoiceRecordEnd: 微信录音的最长时间是1分钟, 超过这个时间录音会自动停止. onVoiceRecordEnd的作用就是注册一个callback, 当录音超时的时候执行.

我想吐槽的是这个API的设计, 我觉得更合理的方式是应该在startRecord的时候就注册进去, 因为这个超时逻辑和stopRecord逻辑有很大程度上是重叠的, 比如错误处理, 重置录音按钮样式等, 而这些逻辑在startRecord的时候就是已知的了.

就是说这个API应该设计成这样:

wx.startRecord({ success: // ... fail: //... complete: //... onVoiceRecordEnd: //... })

但是现在的onVoiceRecordEnd却是一个独立的API:

wx.onVoiceRecordEnd({ // 录音时间超过一分钟没有停止的时候会执行 complete 回调 complete: function (res) { var localId = res.localId; } });

微信是觉得我的WebApp里面只会有一种onVoiceRecordEnd逻辑么? 事实上同一个WebApp的不同页面很可能有不同的onVoiceRecordEnd逻辑.

所以我现在只能在startRecord的技术上由封装了一层, 当startRecord成功的时候将当前的onVoiceRecordEnd逻辑注入到wx.onVoiceRecordEnd中.

onVoicePlayEnd的作用是注册一个callback, 当用户录音播放结束时执行. 它和onVoiceRecordEnd非常类似, 也是一个被处理出来的API. 它应该在playVoice的时候就被注册.

uploadVoice

录音结束后, 你是无法直接访问录音数据的, 需要先调用uploadVoice将数据上传到微信服务器, 然后让你的后台再从微信服务器下载数据… 这个设计直接导致了处理录音数据时间的增加.

微信这么做, 应该是希望保护用户隐私, 但是如果能提供一个新的授权方式(目前只支持两种授权方式, snsapi_basesnsapi_userinfo), 在用户允许录音并且可以直接上传语音到对方服务器, 那就太好了.

Android上, 上传/下载录音时UI会卡住

uploadVoice提供了一个选项isShowProgressTips, 默认为1, 表示显示进度提示. 吐槽1: 但是并没有说不显示进度应该设置几, 虽然0是常用的falsy. 文档不细致, 还要人去试. 吐槽2: 这变量名起的… 我觉得语法有问题,showProgressTip就行了.

这都不重要, 恶心的是: 在Android上, 即使设置了isShowProgressTips为0, 即不显示进度提示, UI依然会被卡住不动, 待上传完之后UI才能继续正常运行. 这个问题在iPhone上就没有.

所以, 比较好的解决方法是, 如果是iPhone则根据需要设置isShowProgressTips, 但Android上最好设置isShowProgressTips为1.

playVoicestopVoice

这对接口有着和startRecordstopRecord一样的问题–调用不能过于频繁否则会玩儿坏微信, 需要在微信的接口上再封装一层以控制调用频率.

假设你录制了多个语音, 在播放其中一个的时候, 播放另一个会产生不确定的结果. 有的时候第二个录音正常播放, 但是有的时候第二个播放不出来. 当你连续播放三个乃至更多的音频的时候问题更多.

还有一个问题就是onVoicePlayEnd在这种情况下也是不稳定的, 连续播放N个音频时, onVoicePlayEnd是不一定触发的…

即使你每次播放录音B之前调用stopVoice停掉正在播放的录音A, 这行为也是不可靠的… 有时候能停下来, 有时候会直接播放录音B的后半截, 并且随后的播放录音行为会乱掉.

这些恶心的bug意味着你必须要限制UI上的操作频率和顺序(比方说当播放一个录音的时候, 其他播放录音按钮是被禁用的), 也就无法创造一个操作流畅(比如随便点击某个录音按钮就可以停掉正在播放的录音而播放新的录音)的用户体验了.

stopVoice的小bug

这个bug跟其他bug比起来可以忽略, 但是仍然蛮影响用户体验, 就是: 在录音播放的最后几秒钟调用stopVoice一次, 会显示ok说明停止录音成功, 但是声音仍然在播放, 继续调用stopVoice会失败, 错误信息为not playing.

(这位处女座请把你手里的板砖放下, 谢谢.)

结语

总之, 微信JSSDK还只是提供了基本功能, 但是健壮性远不足以支撑丰富交互的WebApp. 这种基于微信JSSDK开发的WebApp会有诸多限制, 远无法达到NativeApp的效果.

但是, 基于微信的WebApp有着易访问, 易传播的巨大优势, 可以作为NativeApp开发之前的一个试点项目. 最终为了确保良好的用户体验, NativeApp还是必不可少的.

微信JSSDK与录音相关的坑的更多相关文章

  1. 微信JSSDK与录音相关的坑

    欢迎各位转载, 以让微信团队重视这些恼人的BUG. 请注明出处微信JSSDK与录音相关的坑 by lzl124631x 最近一直在做微信JSSDK与录音相关的功能开发, 遇到了各种奇尺大坑, 时不时冷 ...

  2. 微信JS-SDK选择图片遇到的坑

    微信JS-SDK选择图片遇到的坑 有个需求要在微信企业号里面做开发,有个功能是选择图片,使用input标签肯定是不管用了,Android手机上不能多选,所以使用了微信的JS-SDK提供的相关API,这 ...

  3. 从零开始实现基于微信JS-SDK的录音与语音评价功能

    最近接受了一个新的需求,希望制作一个基于微信的英语语音评价页面.即点击录音按钮,用户录音说出预设的英文,根据用户的发音给出对应的评价.以下是简单的Demo: ![](reecode/qrcode.pn ...

  4. 微信微信JS-SDK 6.0.2 填坑笔记

    0.为什么以前不需要配置这么麻烦就可以修改分享description 等信息,但是现在不行了. 因为6.0.2版本之前没有做权限验证,所以config都是ok,但这并不意味着你config中的签名是O ...

  5. 微信JSSDK录音的一些bug

    UPDATE: 这篇博文已经过期, 新的BUG总结请看微信JSSDK与录音相关的坑 微信JSSDK有不少坑, 最近做一个webapp, 用到了其中的录音功能, 发现不少问题, 总结一下: 当你调用st ...

  6. 【微信JSSDK】PHP版微信录音文件下载

    微信的录音文件上传到微信服务器上,只能保存三天. 因此需要做一个转存到自己服务器,或者七牛云的操作. 转存到自己服务器 调用微信JSSDK API 录音, 录音结束,上传到微信服务器,获取录音文件的 ...

  7. 【转】微信jssdk录音功能开发记录

    转自:http://www.cnblogs.com/liujunyang/p/4962423.html#undefined 0.需求描述 在微信浏览器内打开的页面,制作一个按钮,用户按住按钮后开始录音 ...

  8. 微信JS-SDK 接口调用与 php 遇到的坑

    问题:config:invalid signature一直爆这个错误 解决: 看我把这些坑都总结了一下:要命的invalid signature. https://segmentfault.com/q ...

  9. HTML5微信jssdk录音播放语音的方法

    HTML5微信jssdk录音播放语音的方法需要注意的2个问题1 就是一定要判断1秒内 录音都不算 ps:太短不能录音 2 录音超过1分钟 会发现正在录音突然消失 所以要写wx.onVoiceRecor ...

随机推荐

  1. CentOS7安装后连不上网络无法使用yum

    更新日期:2018年5月31日 笔者今天在本地VMware中安装了CentOS7后,使用yum安装wget的时候发现不能下载,并有下图所示的提示: 于是,笔者就去问度娘,然后就找到了如下各种回复: 1 ...

  2. Dubbo下载-从missing artifactId说起

    项目pom文件引入dubbo 报 missing artifactId https://github.com/dangdangdotcom/dubbox 从GitHub上直接下载解压包, 最好下载分支 ...

  3. ibatis中$和#的区别

    比如当变量name的类型是Stirng时, $name$ 打印出来的是 张三 #name# 打印出来的是 ‘张三’ $ 的作用实际上是字符串拼接 #用于变量替换 那什么时候用$,什么时候 用 # (1 ...

  4. 聊聊MyBatis缓存机制【美团-推荐】

    聊聊MyBatis缓存机制 2018年01月19日 作者: 凯伦 文章链接 18778字 38分钟阅读 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用My ...

  5. hashlib模块 md5 sha1

    Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示) 对于同一个字符串,不管这个字符串有多长 ...

  6. loj#6235. 区间素数个数(min25筛)

    题意 题目链接 Sol min25筛的板子题,直接筛出\(g(N, \infty)\)即可 筛的时候有很多trick,比如只存\(\frac{N}{x}\)的值,第二维可以滚动数组滚动掉 #inclu ...

  7. Django基础八之cookie和session

    一 会话跟踪 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应.例如你给10086打个电话,你就是客户端,而10086服务人员就是服务器 ...

  8. 原型相关的方法isPrototypeOf、Object.getPrototypeOf、hasOwnProperty、Object.getOwnPropertyName、Object.keys

    在看<高程3>第六章的<面向对象的程序设计>的原型那一节时,有一下5个函数,功能较为接近,但是又都很基础,很重要 所以在此,加以说明,以便日后复习 function Perso ...

  9. div+css模拟select下拉框

    <!DOCTYPE html><html ><head lang="zh"> <meta http-equiv="Content ...

  10. 5分钟搞定jQuery+zepto.js+面向对象插件

    今天分享一下快速使用jQuery+zepto.js的技巧,需要的记得收藏 1.jQuery的引入:本地下载jQuery(后面简称jq)的源文件,开发版本使用非min版,线上使用min版,zepto.j ...