FragmentManager.findFragmentByTag()返回null的特定场景排查求助
踩坑记录:FragmentManager.findFragmentByTag() 返回 null 的时机问题
最近碰到一个头疼的问题:调用FragmentManager.findFragmentByTag("tag")居然返回null,直觉告诉我这肯定和调用时机脱不了干系,结合我的代码场景,终于摸清楚了来龙去脉,分享给大家。
先贴一下我的相关代码片段:
网络库回调方法
onStart() { Utils.ShowLoadingDialog("loading"); } onFinish() { Utils.DismissLoadingDialog("loading"); }
Utils类中加载对话框的核心代码(片段)
public void showLoadingDialog(String title, String message, String tag) { DialogFragment loadingDialogFragment = new LoadingDialogFragment(); Bundle args = new Bundle(); args.putString("title", title); args.putString("message", message); loadingDialogFragment.setArguments(args); loadingDialogFragment.show(fragmentManager, tag); } public void dismissLoadingDialog(String tag) { DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(tag); if (dialogFragment != null) { dialogFragment.dismiss(); } }
为什么会返回null?核心是时机不匹配
我排查后发现,主要是这几种场景导致的:
- 宿主页面已经销毁:比如用户在网络请求过程中退到后台、或者关闭了当前Activity/Fragment,这时候
FragmentManager已经把DialogFragment移除了,再调用findFragmentByTag()自然找不到。 - 回调不在主线程:
FragmentManager的所有操作必须在主线程执行,如果网络回调是子线程触发的,直接调用方法会因为线程同步问题拿不到实例,甚至引发崩溃。 - 对话框还没完成添加就调用dismiss:如果网络请求太快,
onStart()刚调用显示对话框,DialogFragment还没完成attach到FragmentManager的流程,onFinish()就触发了关闭,这时候也会找不到。
针对性解决办法
1. 先判断宿主页面是否存活
在关闭对话框前,先检查Activity/Fragment的生命周期状态,避免在销毁后操作:
public void dismissLoadingDialog(String tag, FragmentActivity activity) { // 先判断Activity是否还活着 if (activity == null || activity.isFinishing() || activity.isDestroyed()) { return; } DialogFragment dialogFragment = (DialogFragment) activity.getSupportFragmentManager().findFragmentByTag(tag); if (dialogFragment != null) { dialogFragment.dismiss(); } }
如果是Fragment作为宿主,就检查fragment.isAdded()或者fragment.getContext() != null。
2. 强制在主线程执行回调操作
网络库的回调大概率是在子线程,一定要切换到主线程再操作对话框:
onFinish() { activity.runOnUiThread(() -> { Utils.DismissLoadingDialog("loading", activity); }); }
用Kotlin的话可以用withContext(Dispatchers.Main),RxJava就用observeOn(AndroidSchedulers.mainThread())。
3. 维护对话框状态标记,避免状态不一致
可以在Utils里用一个Map记录当前显示的对话框,防止FragmentManager的状态和我们的操作不同步:
private static final Map<String, DialogFragment> dialogMap = new HashMap<>(); public void showLoadingDialog(String title, String message, String tag, FragmentManager fragmentManager) { if (dialogMap.containsKey(tag)) { return; } DialogFragment loadingDialogFragment = new LoadingDialogFragment(); Bundle args = new Bundle(); args.putString("title", title); args.putString("message", message); loadingDialogFragment.setArguments(args); loadingDialogFragment.show(fragmentManager, tag); dialogMap.put(tag, loadingDialogFragment); // 对话框关闭时移除标记 loadingDialogFragment.setOnDismissListener(dialog -> dialogMap.remove(tag)); } public void dismissLoadingDialog(String tag, FragmentActivity activity) { if (activity == null || activity.isFinishing() || activity.isDestroyed()) { dialogMap.remove(tag); return; } DialogFragment dialogFragment = dialogMap.get(tag); if (dialogFragment != null && dialogFragment.isAdded()) { dialogFragment.dismiss(); } else { // 从FragmentManager再兜底找一次 DialogFragment fmDialog = (DialogFragment) activity.getSupportFragmentManager().findFragmentByTag(tag); if (fmDialog != null) { fmDialog.dismiss(); } dialogMap.remove(tag); } }
4. 绑定View生命周期(Fragment场景专属)
如果是在Fragment里用对话框,建议绑定ViewLifecycleOwner,确保只在View存活时操作:
// 在Fragment中调用时传递ViewLifecycleOwner Utils.showLoadingDialog("加载中", "请稍候...", "loading_tag", getChildFragmentManager(), getViewLifecycleOwner()); // Utils中添加生命周期监听 public void showLoadingDialog(String title, String message, String tag, FragmentManager fragmentManager, LifecycleOwner lifecycleOwner) { DialogFragment loadingDialogFragment = new LoadingDialogFragment(); // 设置参数... loadingDialogFragment.show(fragmentManager, tag); // 监听View销毁事件,自动关闭对话框 lifecycleOwner.getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (loadingDialogFragment.isAdded()) { loadingDialogFragment.dismiss(); } source.getLifecycle().removeObserver(this); } } }); }
内容的提问来源于stack exchange,提问作者Zapnologica
相关产品推荐
相关产品推荐

