Android VideoView播放触发WindowManager$BadTokenException崩溃无法复现求助
崩溃触发原因分析
- 根本原因:从堆栈可以看到,崩溃发生在
VideoView内置的错误回调逻辑中弹出系统默认错误AlertDialog时。你当前重写的onError方法返回值为false,根据Android官方逻辑,当OnErrorListener返回false时,VideoView会自动弹出系统自带的播放错误提示对话框。 - 触发场景:当播放错误回调执行时,
DvdPlayerFragment已经和Activity解绑、或者宿主Activity已经处于销毁/正在销毁状态,此时系统弹窗使用的Window token已经失效,就会抛出BadTokenException。常见的触发时机包括:- 用户进入播放页后立即退出,后台请求视频地址/加载视频的逻辑还在执行,后续错误回调返回时页面已经销毁
- 设备横竖屏切换、分屏等导致Activity重建的场景,旧的Fragment实例已经销毁,但是旧的
VideoView错误回调被触发 - 应用切后台后系统回收了Activity,此时播放错误回调触发
修复方案
核心修复(解决崩溃直接诱因)
修改onError方法的返回值为true,阻止VideoView弹出系统默认错误对话框,所有错误逻辑由你自行处理即可:
@Override public boolean onError(MediaPlayer mp, int i, int j) { Log.d(TAG, "onError: BEGIN"); // 先判断页面存活状态,再执行业务逻辑 if (!isAdded() || getActivity() == null || getActivity().isFinishing() || isDetached()) { Log.d(TAG, "onError: fragment or activity already destroyed, skip logic"); return true; } showProgressIndicator(false); destroyWatchStatsTimer(); updateServer(); // 可以自定义错误提示,比如调用你自己的showMessage方法 showMessage("播放出错,请稍后重试"); Log.d(TAG, "onError: END"); return true; }
补充优化(避免其他潜在问题)
- 增加页面生命周期状态判断
所有涉及UI操作、Dialog/Toast弹出的逻辑,执行前都先判断Fragment是否还附着在Activity上、Activity是否存活,避免无效操作。 - 主动释放资源
在onDestroyView/onDestroy中主动释放VideoView和MediaController资源,取消所有待执行回调:
@Override public void onDestroyView() { super.onDestroyView(); // 释放VideoView资源 if (videoView != null) { videoView.stopPlayback(); videoView.setMediaController(null); videoView.setOnPreparedListener(null); videoView.setOnErrorListener(null); videoView.setOnCompletionListener(null); } // 释放MediaController if (mediaController != null) { mediaController.setAnchorView(null); mediaController = null; } }
- 优化
MediaController显示逻辑
当前你在触摸事件里已经有了部分状态判断,可以再补充Fragment生命周期判断,避免无效调用。
内容的提问来源于stack exchange,提问作者Юлия Белик
相关产品推荐
相关产品推荐

