切换NavigationView后ViewPager2的Fragment空指针崩溃问题求解
问题分析与解决方案
崩溃原因
报错显示BoxAdapter.notifyDataSetChanged()调用时出现空指针,核心原因有两点:
- ViewPager2的
FragmentStateAdapter会在Fragment离屏(切换到其他页面)时销毁其视图,此时OneFragment的adapter变量会因视图销毁失去有效引用;若返回HomeFragment后,OneFragment未被重新加载视图(比如ViewPager2当前显示的是TwoFragment),adapter会保持未初始化的null状态。 HomeFragment直接持有OneFragment的实例引用,当ViewPager2重建Fragment时,该引用可能指向未初始化视图的旧实例,调用clearBox()时触发空指针。
解决方案
方案1:增加空值判断,避免直接调用null对象
修改OneFragment的clearBox()方法,先校验关键变量的有效性,同时在视图销毁时清理引用避免内存泄漏:
public void clearBox() { // 先判断boxList和adapter是否有效 if (boxList != null && adapter != null) { boxList.clear(); adapter.notifyDataSetChanged(); } } @Override public void onDestroyView() { super.onDestroyView(); // 视图销毁时清空引用 adapter = null; recyclerView = null; button = null; linearLayout = null; }
方案2:用ViewModel分离数据与视图,保证数据一致性
通过ViewModel存储boxList,即使视图被销毁,数据也不会丢失,重新加载视图时自动复用数据:
- 创建
OneViewModel:
public class OneViewModel extends ViewModel { private MutableLiveData<List<Box>> boxList = new MutableLiveData<>(); public void initBoxList(int count) { List<Box> list = new ArrayList<>(); for(int i=0; i<count; i++) { list.add(new Box(i+1, Mode.NOTCHECKED)); } boxList.setValue(list); } public void clearBoxList() { List<Box> list = boxList.getValue(); if (list != null) { list.clear(); boxList.setValue(list); } } public LiveData<List<Box>> getBoxList() { return boxList; } }
- 修改
OneFragment,使用ViewModel管理数据:
public class OneFragment extends Fragment { Button button; RecyclerView recyclerView; BoxAdapter adapter; private OneViewModel viewModel; int t; // 推荐用newInstance传递参数,避免Fragment重建时参数丢失 public static OneFragment newInstance(int k) { OneFragment fragment = new OneFragment(); Bundle args = new Bundle(); args.putInt("count", k); fragment.setArguments(args); return fragment; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewModel = new ViewModelProvider(this).get(OneViewModel.class); // 从参数中获取count if (getArguments() != null) { t = getArguments().getInt("count"); } // 仅首次创建时初始化数据 if (viewModel.getBoxList().getValue() == null) { viewModel.initBoxList(t); } } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_one, container, false); linearLayout = view.findViewById(R.id.linearlayout); button = view.findViewById(R.id.click); recyclerView = view.findViewById(R.id.recyclerview); // 观察数据变化,自动更新Adapter viewModel.getBoxList().observe(getViewLifecycleOwner(), newList -> { if (adapter == null) { adapter = new BoxAdapter(getActivity(), newList); recyclerView.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); } }); button.setOnClickListener(v -> Toast.makeText(getActivity(), "您点击了我。T= "+t, Toast.LENGTH_SHORT).show() ); return view; } public void clearBox() { viewModel.clearBoxList(); } public int getBoxSize() { List<Box> list = viewModel.getBoxList().getValue(); return list != null ? list.size() : -1; } @Override public void onDestroyView() { super.onDestroyView(); adapter = null; recyclerView = null; button = null; linearLayout = null; } }
- 修改
HomeFragment中创建OneFragment的方式:
// 替换原来的new OneFragment(q) oneFragment = OneFragment.newInstance(q);
方案3:避免直接持有Fragment引用,通过Adapter获取实例
修改HomeFragment的按钮点击事件,通过ViewPager2的Adapter获取有效OneFragment实例:
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 通过Adapter获取位置0的OneFragment实例 OneFragment targetFragment = (OneFragment) myAdapter.createFragment(0); if (targetFragment != null) { targetFragment.clearBox(); } } });
内容的提问来源于stack exchange,提问作者Xoszagas
相关产品推荐
相关产品推荐

