一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
前言
在实现MediaPlayer
简单使用后在实际开发中还是遇到不少问题,这里对问题做总结以记录原因和解决办法算是开发归纳了。
问题汇总
TrackInfo资源信息查询
在开发过程中加载资源并执行prepareAsync
方法后在回调方法中获取资源TrackInfo。原本希望通过该方法获取到视频尺寸信息,例如MediaExtractor
通过方法getTrackFormat
来获取资源信息。
TrackInfo
内部可获取到MediaFormat
对象,但在实际源码中会发现返回MediaFormat
是个空。
public MediaFormat getFormat() {
if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
|| mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
return mFormat;
}
return null;
}
因此在使用MediaPlayer
内部估计无法直接获取到资源文件则只能通过MediaExtractor
外部获取了。
pause之后start失败
从MediaPlayer
时序图可以看到调用方法,若调用了pause
之后再调用start
是没有问题的。若时序在stop
则必须重新执行prepare
方法等待重新准备资源后再播放。
- 问题1
实际开发中遇到的问题是当播放器的
Activity
处于后台情况时,Activity
会调用到生命周期的onStop
方法,因为MediaPlayer
生命周期和页面绑定了也会调用stop
。最后就知道了原因在哪了MediaPlayer
目前是stop
状态,所以要重新为MediaPlayer
执行prepare
方法播放。
问题1是解决了后台返回后播放器可以重新播放资源,但是绑定SurfaceView
却是黑屏情况。
- 问题2
绑定
SurfaceView
黑屏情况实质上和问题1相似。因为SurfaceView
同样也有生命周期,当Activity
处于后台后调用了surfaceDestroyed
,原先SurfaceView
和播放器绑定的Surface
已经失效需要重新再绑定处理。
原逻辑是在post
方法中只初始化一次并绑定,这样看来只在post
方法中绑定做法并不合适。
surfaceView.post(new Runnable() {
@Override
public void run() {
AndroidMediaPlayer.Builder builder = new AndroidMediaPlayer.Builder(TestSimpleMediaPlayerActivity.this,uri,listener);
builder.withSurface(surfaceView.getHolder().getSurface());
androidMediaPlayer = builder.createPlayer();
androidMediaPlayer.prepareAsync();
}
});
从surfaceView
的回调方法中surfaceCreated
进行Surface
绑定工作,从而保证每次从后台进入页面都能刷新绑定。
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if(androidMediaPlayer != null)
androidMediaPlayer.setSurface(surfaceView.getHolder().getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
Log.d("<> surfaceView","surfaceChanged");
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
Log.d("<> surfaceView","surfaceDestroyed");
}
});
- 问题3
MediaPlayer
还有个问题是没有提供播放进度回调接口。虽然MediaPlayer
有getCurrentPosition
方法可以获取到当前播放进度,但无法封装提供做到实时获取的能力,因此需要开发者自行实现实时获取的方法。
通过Thread
不断循环调用播放器getCurrentPosition
方法获取到当前播放毫秒值,另外也可以根据实际业务需要添加延迟处理。
private class PlayerThread extends Thread{
private boolean toGet = false;
public void startGet(){
toGet = true;
}
public void stopGet(){
toGet = true;
}
@Override
public void run() {
super.run();
while (toGet){
if(mMediaPlayer != null){
int position = mMediaPlayer.getCurrentPosition();
if(mListener != null){
playerInfo.playerProgress = position;
mListener.onPlayerInfoCallBack(playerInfo);
}
}
}
}
}
总结
MediaPlayer
使用虽然简单,但一些细节问题在实现开发中还是会暴露出来。若真正要开发实现完成完整播放器功能还是需要完善和考虑许多功能场景,MediaPlayer
还是有一些底层能力可以深挖。