Toolbar返回图标双击触发NullPointerException:FragmentActivity.onBackPressed()异常
解决Toolbar返回图标点击两次后崩溃的NullPointerException问题
你的问题核心在于Fragment销毁后,之前设置的Toolbar导航点击监听器仍持有Fragment引用,导致调用getActivity()时返回null,进而触发空指针异常。
问题根源分析
你的ImageDetailFragment在onCreateView中直接给宿主PagesActivity的Toolbar设置了新的NavigationOnClickListener,但当Fragment被弹出栈(第一次点击返回回到PagesActivity),Fragment的生命周期走到onDestroyView,此时Fragment已和Activity解除关联,getActivity()会返回null。但你设置的Toolbar点击监听器并未被移除,第二次点击返回按钮时,触发的还是Fragment里的旧监听器,这时候调用getActivity().onBackPressed()自然会抛出NPE。
另外,PagesActivity本身已经通过onOptionsItemSelected处理了Toolbar返回按钮的逻辑,Fragment重新设置监听器会和Activity的原有逻辑冲突。
解决方案:恢复Toolbar原有监听器
我们需要在Fragment销毁视图时,把Toolbar的导航点击监听器恢复成Activity原来的状态,同时在调用getActivity()前做非空判断。
修改ImageDetailFragment的代码,添加以下逻辑:
package com.example.android.evilsecretgallery; import android.graphics.Bitmap; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.transition.TransitionInflater; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; import com.github.chrisbanes.photoview.PhotoView; public class ImageDetailFragment extends Fragment { private static final String EXTRA_IMAGE = "image_item"; private static final String EXTRA_TRANSITION_NAME = "transition_name"; // 保存Activity原来的Toolbar导航监听器 private View.OnClickListener originalToolbarNavListener; public ImageDetailFragment() { // Required empty public constructor } public static ImageDetailFragment newInstance(ImageModel image, String transitionName) { ImageDetailFragment fragment = new ImageDetailFragment(); Bundle args = new Bundle(); args.putParcelable(EXTRA_IMAGE, image); args.putString(EXTRA_TRANSITION_NAME, transitionName); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); postponeEnterTransition(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move)); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_image_detail, container, false); Toolbar toolbar = getActivity().findViewById(R.id.toolbar); // 保存Activity原来的导航监听器 originalToolbarNavListener = toolbar.getNavigationOnClickListener(); toolbar.setTitle(" ES "); toolbar.setTitleTextAppearance(getActivity(), R.style.EvilTextAppearance); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 先判断Activity是否存在,避免NPE AppCompatActivity hostActivity = (AppCompatActivity) getActivity(); if (hostActivity != null) { hostActivity.onBackPressed(); } } }); return rootView; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final ImageModel image = getArguments().getParcelable(EXTRA_IMAGE); String transitionName = getArguments().getString(EXTRA_TRANSITION_NAME); final PhotoView imageView = view.findViewById(R.id.detail_image); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { imageView.setTransitionName(transitionName); } Glide.with(getActivity()) .asBitmap() .load(image.getUrl()) .into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { startPostponedEnterTransition(); imageView.setImageBitmap(resource); } }); } @Override public void onDestroyView() { super.onDestroyView(); Toolbar toolbar = getActivity().findViewById(R.id.toolbar); if (toolbar != null) { // 恢复Activity原来的Toolbar导航监听器 toolbar.setNavigationOnClickListener(originalToolbarNavListener); } } }
额外优化建议
- 如果你使用AndroidX Fragment,可以用
requireActivity()代替getActivity(),它会在Activity为null时抛出更明确的IllegalStateException,方便调试,但仍建议在调用前做非空判断。 - 复杂场景下,尽量避免Fragment直接修改宿主Activity的UI组件逻辑,可以通过接口回调或者ViewModel让Activity统一处理导航逻辑,减少组件耦合。
内容的提问来源于stack exchange,提问作者Simone Atzeni
相关产品推荐
相关产品推荐

