«

Android的support v4中的Fragment的一个Bug

时间:2024-3-2 18:02     作者:韩俊     分类: Android


问题描述

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();
        }
    }

标签: android

热门推荐