Android-使用MediaPlayer播放网络音频并且缓存

需求

  • 接口中返回一个List,其中部分对象有音频文件的url.对有url的item视图中添加一个播放按钮.点击按钮播放音频,再次点击暂停或停止.点击其他item播放新的音频.点击item进入二级页面,同样可以播放这个音频,需要增加一个播放动态效果并且显示音频文件时长.

实现

  • 点击播放按钮时,使用MediaPlayer加载url并且播放,简单代码:
MediaPlayer mediaPlayer;
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(url);//设置播放来源
mediaPlayer.prepareAsync();//异步准备
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                //异步准备监听
               
                public void onPrepared(MediaPlayer mediaPlayer) {
                    LogUtils.debugLongInfo("Voice异步文件准备完成");
                    LogUtils.debugLongInfo("Voice异步文件时长", mediaPlayer.getDuration() / 1000 + "");
                    mediaPlayer.start();//播放
                }
            });
mediaPlayer.setScreenOnWhilePlaying(true);// 设置播放的时候一直让屏幕变亮
mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
                //文件缓冲监听
               
                public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
                    LogUtils.debugLongInfo("Voice进度", i + "%");
                    LogUtils.debugLongInfo("Voice文件长度", mediaPlayer.getDuration() / 1000 + "");
                }
            });

遇到的问题

  • 使用MediaPlayer可以直接播放url音频,但是网络状态较差的情况下,加载时间长,用户体验差.而且在item中用户无法直接观察到文件加载进度,会认为点击没反应.在二级页面中只有加载完成后才能获取音频文件的时长并且展示文件时长.

解决思路

  • 加载item的时候提前把url中的音频加载到本地(内存或储存中),点击的时候直接播放加载好的文件.

尝试

1.每个item的holder中加载一个MediaPlayer,提前异步加载,点击的时候再播放.
结果:会报错 MediaPlayer finalized without being released
2.列表页面不做提前加载操作.二级页面在onCreate中加载MediaPlayer,但是只有点击的时候才播放.
结果:可行,但是又回到原点,用户体验不好.
3.使用网络提前在holder中将url的File下载到本地文件夹中,播放的时候检查本地缓存中是否有这个文件,有的话播放本地声音文件,没有的话再播放网络文件.
结果:自己搭建一个网络下载缓存框架成本太高,而且如果用户加载到没有缓存的内容体验还是很差.而且MediaPlayer播放网络音频和本地音频方法不同,没必要写多余的判断走不同的代码逻辑.

最终思路

列表页面加载出item的时候就应该已经异步加载url中的文件.点击播放的时候使用直接播放本地文件.(我的业务场景中声音文件都很小,60s以内下载也比较快).简单参考Glide的加载缓存模式
发现了一个AndroidVoice缓存库
AndroidVideoCache
使用详解
视频缓存AndroidVideoCache攻略
参考文章
Android 使用MediaPlayer播放网络音频
AndroidVideoCache库使用了一种代理方式,如果文件缓存在了本地,仍然会提供一个url地址.此时页面中只需要使用播放url的方法就可以使用AndroidVideoCache的本地url进行播放.

需要注意的问题

1.AndroidVideoCache框架不支持边播边缓存
2.不支持m3u8格式(其他不支持格式未知,我使用.mp3文件没问题)
3.不支持HTTPS 解决办法:Android 9.0 使用AndroidVideoCache时不能缓存视频
其他问题详看issues

我这个需求比较简单,简单实现一个demo即可.现在音视频类APP越来越多,这类需求也越发复杂,有更好的解决方式还请留言告知,以便帮助更多人.

MediaPlayer常用方法,注意调用顺序错误会报异常

int getCurrentPosition();// 得到当前播放位置(ms)
int getDuration();// 得到文件的时间(ms)
void setLooping(boolean var1);// 设置是否循环播放
boolean isLooping();// 是否循环播放
boolean isPlaying();// 是否正在播放
void pause();// 暂停
void prepare();// 同步准备
void prepareAsync();// 异步准备
void release();// 释放MediaPlayer对象
void reset();// 重置MediaPlayer对象
void seekTo(int msec);// 指定播放位置(以毫秒为单位)
void setDataSource(String path);// 设置播放资源
void setScreenOnWhilePlaying(boolean screenOn);// 设置播放的时候一直让屏幕变亮
void setWakeMode(Context context, int mode);// 设置唤醒模式
void setVolume(float leftVolume, float rightVolume);// 设置音量,参数分别表示左右声道声音大小,取值范围为0~1
void start();// 开始播放
void stop();// 停止播放

void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener);//文件缓冲监听
void setOnPreparedListener(MediaPlayer.OnPreparedListener listener);//异步准备监听
void setOnCompletionListener(MediaPlayer.OnCompletionListener listener);//播放完成监听
void setOnErrorListener(MediaPlayer.OnErrorListener listener);//播放异常监听

自己也维护了一个库,逐步完善中

SPlayer

欢迎使用

© 著作权归作者所有
这个作品真棒,我要支持一下!
大话安卓群友文章分享,包含一些技术讨论、新框架介绍
0条评论
top Created with Sketch.