问题描述
public class MatchFragment extends BaseFragment { public static final String TAG = MatchFragment.class.getSimpleName(); private FragmentManager mFragmentManager; public MatchFragment() { // Required empty public constructor } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_match, container, false); mFragmentManager = getChildFragmentManager(); FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); fragmentTransaction.replace(R.id.root_frame_layout, new MatchingFashionFragment()); fragmentTransaction.commit(); return view; } }
当这个Fragment对象被嵌入到一个Activity中然后又被其他Fragment取代后,然后这个Fragment对象又被重新放回到Activity中时,在fragmentTransaction.commit();处会报如下的错误
java.lang.IllegalStateException: Activity has been destroyed at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1365) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574) at com.jd.wxsq.app.Fragment.MatchFragment.onCreateView(MatchFragment.java:70) at android.support.v4.app.Fragment.performCreateView(Fragment.java:1500) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5017) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) at dalvik.system.NativeStart.main(Native Method)
问题原因分析
查看android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1365)处的源码:
public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mActivity == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } mPendingActions.add(action); if (mPendingActions.size() == 1) { mActivity.mHandler.removeCallbacks(mExecCommit); mActivity.mHandler.post(mExecCommit); } } }
在第7行,可以看到mActivity为null了,所以可以猜测Fragment被detach后,Fragment的mChildFragmentManager的mActivity变为空了,而Fragment被attach后,mChildFragmentManager的mActivity又没有被正确的赋予Activity的对象,才造成了这个bug
在哪里被赋值成null呢?请看下面的代码
C:UserslihuapingAppDataLocalAndroidsdkandroid-sdkextrasandroidm2repositorycomandroidsupportsupport-v419.0.0support-v4-19.0.0-sources.jar!androidsupportv4appFragmentManager.java
public void dispatchDestroy() { mDestroyed = true; execPendingActions(); moveToState(Fragment.INITIALIZING, false); mActivity = null; mContainer = null; mParent = null; }
所以我们要做的就是,在Fragment被detach时,把mChildFragmentManager置空就可以了,mChildFragmentManager是Fragment的私有成员,如何做到?使用反射就可以做到
在你的Fragment的代码中加入如下的代码:
@Override public void onDetach() { super.onDetach(); try { Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager"); childFragmentManager.setAccessible(true); childFragmentManager.set(this, null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }