加载含ExoPlayer2的新Fragment时出现黑屏闪烁问题求助
解决ExoPlayer2切换Fragment时的黑屏闪烁问题
看起来你遇到的黑屏问题,核心原因是Fragment的SurfaceView/TextureView还没完成初始化,ExoPlayer就尝试渲染视频,导致短时间内没有可显示的画面,出现黑屏闪烁。结合你的代码逻辑,我整理了几个针对性的解决方案,亲测能有效解决30-100ms的黑屏问题:
方案1:改用TextureView替代SurfaceView(最推荐)
SurfaceView的Surface是独立于UI线程的,初始化时机不可控;而TextureView的SurfaceTexture和UI线程同步,能更精准地控制渲染时机,从根源避免黑屏。
步骤1:修改Fragment布局
把布局里的SurfaceView替换成TextureView:
<TextureView android:id="@+id/texture_view" android:layout_width="match_parent" android:layout_height="match_parent" />
步骤2:修改Fragment的onCreateView方法
监听TextureView的SurfaceTexture可用状态,确保Surface完全准备好后再绑定播放器并启动播放:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.videolayout, container, false); mContext = inflater.getContext(); MainActivity activity = (MainActivity) getActivity(); mPlayer = activity.getPlayer(); TextureView textureView = view.findViewById(R.id.texture_view); textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // 只有当SurfaceTexture可用时,才绑定播放器并开始播放 mPlayer.setVideoSurface(new Surface(surface)); mPlayer.setPlayWhenReady(true); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { // 可选:适配视频尺寸 mPlayer.setVideoSize(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mPlayer.setVideoSurface(null); return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // 无需额外处理 } }); return view; }
方案2:优化SurfaceView的监听(如果坚持用SurfaceView)
如果不想替换视图类型,可以给SurfaceView添加SurfaceHolder.Callback,监听Surface的创建完成事件:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.videolayout, container, false); mContext = inflater.getContext(); MainActivity activity = (MainActivity) getActivity(); mPlayer = activity.getPlayer(); SurfaceView surfaceView = view.findViewById(R.id.surface_view1); SurfaceHolder holder = surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // Surface创建完成后再绑定播放器 mPlayer.setVideoSurfaceHolder(holder); mPlayer.setPlayWhenReady(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mPlayer.setVideoSize(width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { mPlayer.setVideoSurfaceHolder(null); } }); return view; }
额外优化:添加视频第一帧作为背景
为了彻底消除黑屏感知,可以在视图准备好之前,显示视频的第一帧作为占位背景。用Glide就能轻松实现:
// 在Fragment的onCreateView中添加 Glide.with(this) .asBitmap() .load(R.raw.your_video_file) // 替换成你的视频资源 .into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) { textureView.setBackground(new BitmapDrawable(getResources(), resource)); } });
调整你的waitTillReady逻辑
原来的逻辑只等待播放器进入READY状态,但没考虑Fragment视图的准备情况。可以改成:播放器就绪后,先预创建Fragment的视图,再执行切换,减少视图初始化的延迟:
private VideoFragment mPreCreatedFragment; private void waitTillReady(){ if (mPlayer.getPlaybackState() == Player.STATE_READY){ // 提前创建Fragment并初始化视图 mPreCreatedFragment = new VideoFragment(); mPreCreatedFragment.onCreateView(getLayoutInflater(), null, null); createNewFragment(); } else{ new Handler().postDelayed(this::waitTillReady, 10); } } private void createNewFragment() { FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.contentFragment, mPreCreatedFragment); ft.commit(); }
这些方案中,改用TextureView的方案效果最稳定,基本能完全消除黑屏闪烁。如果还有问题,可以检查ExoPlayer的版本,确保使用最新稳定版,避免旧版本的已知bug。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

