[英]Android VideoView crash in onPrepared
在我的应用程序中,我在RecyclerView列表中有一堆VideoViews。 从MediaPlayer接收onPrepared回调时,有时我会在Android SDK的VideoView中崩溃。 异常本身是在本机代码中触发的。 这种情况仅偶尔发生,我无法可靠地重现。
Fatal Exception: java.lang.IllegalStateException
media.MediaPlayer.getVideoWidth (MediaPlayer.java)
android.widget.VideoView$2.onPrepared (VideoView.java:422)
android.media.MediaPlayer$EventHandler.handleMessage (MediaPlayer.java:2208)
android.os.Looper.loop (Looper.java:136)
android.app.ActivityThread.main (ActivityThread.java:5086)
java.lang.reflect.Method.invokeNative (Method.java)
在我看来,这是由MediaPlayer开始加载视频引起的,然后在加载视频之前以某种方式使它无效。 尽管无效,但仍然会调用onPrepared,并且VideoView会尝试访问视频宽度,从而导致异常。 VideoView.java的相关源代码为:
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mCurrentState = STATE_PREPARED;
// Get the capabilities of the player for this stream
Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
MediaPlayer.BYPASS_METADATA_FILTER);
if (data != null) {
mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
|| data.getBoolean(Metadata.PAUSE_AVAILABLE);
mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
|| data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
|| data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
} else {
mCanPause = mCanSeekBack = mCanSeekForward = true;
}
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mMediaPlayer);
}
if (mMediaController != null) {
mMediaController.setEnabled(true);
}
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
if (seekToPosition != 0) {
seekTo(seekToPosition);
}
... etc.
我试图通过仅在收到onPrepared()之后才调用stopPlayback()来修复它,但是它没有帮助。
还有其他解决方案吗?
我通过扩展VideoView,使用反射来访问VideoView的私有变量,并用包装监听器替换onPrepared监听器,从而破解了一个(非常丑陋的)解决方法。 包装程序在将回调传递给VideoView自己的侦听器之前检查IllegalStateExceptions。
public class CustomVideoView extends VideoView {
public CustomVideoView(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init2();
}
public CustomVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init2();
}
public CustomVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
init2();
}
public CustomVideoView(Context context) {
super(context);
init2();
}
private void init2() {
final SurfaceHolder.Callback mSHCallback=getPrivateVar("mSHCallback",SurfaceHolder.Callback.class);
if (mSHCallback==null) return;
SurfaceHolder.Callback wrapper=new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mSHCallback.surfaceDestroyed(holder);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mSHCallback.surfaceCreated(holder);
postOpenVideo();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mSHCallback.surfaceChanged(holder, format, width, height);
}
};
getHolder().addCallback(wrapper);
}
@Override
public void setVideoURI(Uri uri, Map<String, String> headers) {
super.setVideoURI(uri, headers);
postOpenVideo();
}
@Override
public void resume() {
super.resume();
postOpenVideo();
}
protected void postOpenVideo() {
final MediaPlayer mMediaPlayer=getPrivateVar("mMediaPlayer",MediaPlayer.class);
final MediaPlayer.OnPreparedListener mPreparedListener=
getPrivateVar("mPreparedListener",MediaPlayer.OnPreparedListener.class);
if (mPreparedListener==null||mMediaPlayer==null) return;
MediaPlayer.OnPreparedListener wrapper=new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
try {
mp.getVideoWidth();
mPreparedListener.onPrepared(mp);
} catch (IllegalStateException e) {
}
}
};
mMediaPlayer.setOnPreparedListener(wrapper);
}
private <T> T getPrivateVar(String varName, Class<T> clazz) {
try {
Field field = VideoView.class.getDeclaredField(varName);
field.setAccessible(true);
Object value = field.get(this);
field.setAccessible(false);
return clazz.cast(value);
} catch (NoSuchFieldException e) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (ClassCastException e) {
return null;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.