Android MediaPlayer播放超100MB远程音频SeekBar加载不全问题
问题产生原因
- API使用不符合设计规范:当前代码对远程网络音频资源调用了同步方法
mediaPlayer.prepare(),该方法的设计适用场景是本地音频文件,处理网络流时不仅会阻塞UI线程触发ANR,系统还对prepare阶段的网络缓冲大小设置了约100MB的默认阈值:当缓冲数据量达到阈值后,MediaPlayer会直接结束prepare流程进入可播放状态,不会在该阶段完成全文件加载,也无法在此时获取到远程文件的真实总长度。小体积音频文件可以在阈值内完成全量加载,因此不会触发该异常。 - 缓冲进度计算逻辑未适配分段加载机制:对于超过单块缓冲阈值的远程音频,MediaPlayer会自动发起HTTP Range分段请求加载资源,其
OnBufferingUpdateListener回调返回的缓冲百分比,默认以当前正在加载的分块大小作为计算分母,而非整个音频文件的总大小。单块内容加载完成后回调就会返回100%,但此时仅完成了单个分块的加载,并非全文件加载完成;如果加载进度SeekBar直接绑定该回调值、没有做跨分块的进度累计,就会出现进度停在部分位置、无法展示完整加载状态的现象。小体积文件无需触发分段加载逻辑,因此进度显示正常。 - 进度条最大值设置时机错误:当前代码在prepare执行完成后就会获取音频总大小/总时长作为加载SeekBar的max值,此时大文件仅完成了初始分块的加载,MediaPlayer尚未拿到HTTP响应头里完整的Content-Length信息,会将当前已加载分块的长度误判为文件总长度,后续加载新分块时如果没有同步更新SeekBar的max值,自然无法展示完整的加载进度。
- 系统ROM定制逻辑放大问题:绝大多数安卓厂商的定制系统都会对原生MediaPlayer的网络缓冲逻辑做修改,100MB是非常普遍的单缓冲块大小阈值,会进一步提升大文件场景下分段加载逻辑的触发概率,导致进度显示异常更易复现。
代码中将废弃的
setAudioStreamType替换为AudioAttributes的写法符合官方要求,但网络音频场景必须将同步prepare()替换为异步prepareAsync(),配合setOnPreparedListener监听准备完成事件,从根源上避免缓冲阈值带来的各类异常。对应问题代码参考如下:mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).setUsage(AudioAttributes.USAGE_MEDIA).build()); try { mediaPlayer.setDataSource(URL); // 在线流禁止使用同步prepare() mediaPlayer.prepareAsync(); } catch (IllegalArgumentException | IllegalStateException | IOException e1) { e1.printStackTrace(); }
内容的提问来源于stack exchange,提问作者Fatih Demir
相关产品推荐
相关产品推荐

