Android 进阶——你所知道的Fragment或许比你想象中要更强大

引言

Fragment中文翻译为碎片,在实际的开发中我们常把它看成是Activity的一个模块,更多的时候作用当成一个View,常常忽略了它是具备和Activity息息相关的生命周期管理,从而没有真正的掌握和了解Fragment,导致在听说一个Activity+N个Fragement的架构(比如知乎App)的App时觉得似乎意义不大或在碰到Glide 使用Fragemnt进行生命周期管理时无法理解,希望这篇文章能给一些新的启迪。

一、Fragment概述

源码分析基于androidx.fragment.app.Fragment

1、生命周期

在这里插入图片描述
以上是Fragment的标准流程图,在不同情况下当Activity 进入Destroyed状态时,onDestroyView、onDestroy、onDetach并不会每次都会顺序执行

方法 功能说明
onAttach 在Fragment与Activity关联之后被调用。虽然初始化Fragment参数可以通过getArguments()获得,但当Fragment关联到Activity之后,就无法再调用setArguments(),所以除了在最开始时,其它时间都无法向初始化参数添加内容
onCreate Fragment初次创建时调用。但此刻Activity还没有创建完成(此时无法获取Activity的一些资源),因为我们的Fragment也是Activity创建的一部分。
onCreateView 主要用于创建Fragment自身的UI,只做一件事:那就是把xml映射为ViewGroup并返回,若是无界面的可返回Null,此时findViewById可能是找不到的
onViewCreated Fragemnt的View创建完毕之后,通常是在onViewCreated方法中去findViewById的
onActivityCreated 在Activity的OnCreate()结束后,会调用此方法。即此时Fragment和Activity均已创建完毕。所以我们可以在这个方法里使用Activity的所有资源
onStart 当Fragment对用户就是可见时,但用户还未开始与Fragment交互。因为是与Activity的OnStart()绑定的。写在Activity的OnStart()中处理的逻辑,换成用Fragment来实现时,依然可以放在OnStart()中来处理。
onResume 当Fragment对用户可见并且正在运行时调用。即Fragment与用户交互之前的最后一个回调。与onStart类似,也是与Activity的OnResume是相互绑定的,它依赖于包含它的Activity的Activity.onResume。当OnResume()运行完毕之后,就可以正式与用户交互了
onPause 与Activity的OnPause()绑定,意义也一样。当用户离开Fragment时第一个调用这个方法(但并不总意味着Fragment将被销毁)。按照经验,当用户结束会话之前,我们可以在这提交一些变化
onStop 与Activity绑定,已停止的Fragment可以直接返回到OnStart()回调,然后调用OnResume()。
onDestroyView 当Fragment将被结束或者保存,那么下一个回调将是onDestoryView(),将会删除在onCreateView创建的视图。下次这个Fragment若要显示,将会重新创建新视图。这会在onStop之后和onDestroy之前调用。经测试这个方法的调用同onCreateView是否返回非null视图无关。它会在的在这个视图状态被保存之后以及它被它的父Activity回收之前调用
onDestory 当Fragment不再使用时被调用。但即使调用了onDestroy()阶段,仍可以从其依附的父Activity中找到,因为它还没有Detach。
onDetach 当Fragment和Activity分离的时候调用,Fragment就不再与Activity相绑定,它也不再拥有视图层次结构,它的所有资源都将被释放

3、生命周期的管理概述

在这里插入图片描述
在Fragment内部中是通过五个常量定义不同的状态,默认状态设置为INITIALIZING。

static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // Fully created, not started.
static final int STARTED = 3;          // Created and started, not resumed.
static final int RESUMED = 4;          // Created started and resumed.

当Fragment的状态不是INITIALIZING时,通过FragmentManagerImpl来调用对应的周期方法(比如androidx.fragment.app.FragmentManagerImpl#dispatchCreate就是触发onCreate的回调),然后传入当前状态并通过FragmentManagerImpl#dispatchStateChange进行统一改变,进入到androidx.fragment.app.FragmentManagerImpl#moveToState(int newState, boolean always),再通过androidx.fragment.app.FragmentManagerImpl#moveFragmentToExpectedState方法进行状态切换,最终进入到Fragment中核心的周期管理方法androidx.fragment.app.FragmentManagerImpl#moveToState(androidx.fragment.app.Fragment, int, int, int, boolean)

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
                     boolean keepActive) {
        // Fragments that are not currently added will sit in the onCreate() state.
        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
            newState = Fragment.CREATED;
        }
        if (f.mRemoving && newState > f.mState) {
            if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) {
                // Allow the fragment to be created so that it can be saved later.
                newState = Fragment.CREATED;
            } else {
                // While removing a fragment, we can't change it to a higher state.
                newState = f.mState;
            }
        }
        // Defer start if requested; don't allow it to move to STARTED or higher
        // if it's not already started.
        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.ACTIVITY_CREATED) {
            newState = Fragment.ACTIVITY_CREATED;
        }
        // Don't allow the Fragment to go above its max lifecycle state
        // Ensure that Fragments are capped at CREATED instead of ACTIVITY_CREATED.
        if (f.mMaxState == Lifecycle.State.CREATED) {
            newState = Math.min(newState, Fragment.CREATED);
        } else {
            newState = Math.min(newState, f.mMaxState.ordinal());
        }
        if (f.mState <= newState) {
            // For fragments that are created from a layout, when restoring from
            // state we don't want to allow them to be created until they are
            // being reloaded from the layout.
            if (f.mFromLayout && !f.mInLayout) {
                return;
            }
            if (f.getAnimatingAway() != null || f.getAnimator() != null) {
                // The fragment is currently being animated...  but!  Now we
                // want to move our state back up.  Give up on waiting for the
                // animation, move to whatever the final state should be once
                // the animation is done, and then we can proceed from there.
                f.setAnimatingAway(null);
                f.setAnimator(null);
                moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
            }
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    if (newState > Fragment.INITIALIZING) {
                        if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                        if (f.mSavedFragmentState != null) {
                            f.mSavedFragmentState.setClassLoader(mHost.getContext()
                                    .getClassLoader());
                            f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                    FragmentManagerImpl.VIEW_STATE_TAG);
                            Fragment target = getFragment(f.mSavedFragmentState,
                                    FragmentManagerImpl.TARGET_STATE_TAG);
                            f.mTargetWho = target != null ? target.mWho : null;
                            if (f.mTargetWho != null) {
                                f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                        FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                            }
                            if (f.mSavedUserVisibleHint != null) {
                                f.mUserVisibleHint = f.mSavedUserVisibleHint;
                                f.mSavedUserVisibleHint = null;
                            } else {
                                f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                                        FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                            }
                            if (!f.mUserVisibleHint) {
                                f.mDeferStart = true;
                                if (newState > Fragment.ACTIVITY_CREATED) {
                                    newState = Fragment.ACTIVITY_CREATED;
                                }
                            }
                        }
						//f 为Fragment ,mHost 是在FragemntActivity 中实现的一个回调类
                        f.mHost = mHost;
                        f.mParentFragment = mParent;
                        f.mFragmentManager = mParent != null
                                ? mParent.mChildFragmentManager : mHost.mFragmentManager;

                        // If we have a target fragment, push it along to at least CREATED
                        // so that this one can rely on it as an initialized dependency.
                        if (f.mTarget != null) {
                            if (mActive.get(f.mTarget.mWho) != f.mTarget) {
                                throw new IllegalStateException("Fragment " + f
                                        + " declared target fragment " + f.mTarget
                                        + " that does not belong to this FragmentManager!");
                            }
                            if (f.mTarget.mState < Fragment.CREATED) {
                                moveToState(f.mTarget, Fragment.CREATED, 0, 0, true);
                            }
                            f.mTargetWho = f.mTarget.mWho;
                            f.mTarget = null;
                        }
                        if (f.mTargetWho != null) {
                            Fragment target = mActive.get(f.mTargetWho);
                            if (target == null) {
                                throw new IllegalStateException("Fragment " + f
                                        + " declared target fragment " + f.mTargetWho
                                        + " that does not belong to this FragmentManager!");
                            }
                            if (target.mState < Fragment.CREATED) {
                                moveToState(target, Fragment.CREATED, 0, 0, true);
                            }
                        }

                        dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
                        f.performAttach();
                        if (f.mParentFragment == null) {
                            mHost.onAttachFragment(f);
                        } else {
                            f.mParentFragment.onAttachFragment(f);
                        }
                        dispatchOnFragmentAttached(f, mHost.getContext(), false);

                        if (!f.mIsCreated) {
                            dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
                            f.performCreate(f.mSavedFragmentState);
                            dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                        } else {
                            f.restoreChildFragmentState(f.mSavedFragmentState);
                            f.mState = Fragment.CREATED;
                        }
                    }
                    // fall through
                case Fragment.CREATED:
                    // We want to unconditionally run this anytime we do a moveToState that
                    // moves the Fragment above INITIALIZING, including cases such as when
                    // we move from CREATED => CREATED as part of the case fall through above.
                    if (newState > Fragment.INITIALIZING) {
                        ensureInflatedFragmentView(f);
                    }

                    if (newState > Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                if (f.mContainerId == View.NO_ID) {
                                    throwException(new IllegalArgumentException(
                                            "Cannot create fragment "
                                                    + f
                                                    + " for a container view with no id"));
                                }
                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                                if (container == null && !f.mRestored) {
                                    String resName;
                                    try {
                                        resName = f.getResources().getResourceName(f.mContainerId);
                                    } catch (Resources.NotFoundException e) {
                                        resName = "unknown";
                                    }
                                    throwException(new IllegalArgumentException(
                                            "No view found for id 0x"
                                                    + Integer.toHexString(f.mContainerId) + " ("
                                                    + resName
                                                    + ") for fragment " + f));
                                }
                            }
                            f.mContainer = container;
                            f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                f.mInnerView = f.mView;
                                f.mView.setSaveFromParentEnabled(false);
                                if (container != null) {
                                    container.addView(f.mView);
                                }
                                if (f.mHidden) {
                                    f.mView.setVisibility(View.GONE);
                                }
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                        false);
                                // Only animate the view if it is visible. This is done after
                                // dispatchOnFragmentViewCreated in case visibility is changed
                                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
                                        && f.mContainer != null;
                            } else {
                                f.mInnerView = null;
                            }
                        }

                        f.performActivityCreated(f.mSavedFragmentState);
                        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                        if (f.mView != null) {
                            f.restoreViewState(f.mSavedFragmentState);
                        }
                        f.mSavedFragmentState = null;
                    }
                    // fall through
                case Fragment.ACTIVITY_CREATED:
                    if (newState > Fragment.ACTIVITY_CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                        f.performStart();
                        dispatchOnFragmentStarted(f, false);
                    }
                    // fall through
                case Fragment.STARTED:
                    if (newState > Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                        f.performResume();
                        dispatchOnFragmentResumed(f, false);
                        f.mSavedFragmentState = null;
                        f.mSavedViewState = null;
                    }
            }
        } else if (f.mState > newState) {
            switch (f.mState) {
                case Fragment.RESUMED:
                    if (newState < Fragment.RESUMED) {
                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                        f.performPause();
                        dispatchOnFragmentPaused(f, false);
                    }
                    // fall through
                case Fragment.STARTED:
                    if (newState < Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                        f.performStop();
                        dispatchOnFragmentStopped(f, false);
                    }
                    // fall through
                case Fragment.ACTIVITY_CREATED:
                    if (newState < Fragment.ACTIVITY_CREATED) {
                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
                        if (f.mView != null) {
                            // Need to save the current view state if not
                            // done already.
                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
                                saveFragmentViewState(f);
                            }
                        }
                        f.performDestroyView();
                        dispatchOnFragmentViewDestroyed(f, false);
                        if (f.mView != null && f.mContainer != null) {
                            // Stop any current animations:
                            f.mContainer.endViewTransition(f.mView);
                            f.mView.clearAnimation();
                            AnimationOrAnimator anim = null;
                            // If parent is being removed, no need to handle child animations.
                            if (f.getParentFragment() == null || !f.getParentFragment().mRemoving) {
                                if (mCurState > Fragment.INITIALIZING && !mDestroyed
                                        && f.mView.getVisibility() == View.VISIBLE
                                        && f.mPostponedAlpha >= 0) {
                                    anim = loadAnimation(f, transit, false,
                                            transitionStyle);
                                }
                                f.mPostponedAlpha = 0;
                                if (anim != null) {
                                    animateRemoveFragment(f, anim, newState);
                                }
                                f.mContainer.removeView(f.mView);
                            }
                        }
                        f.mContainer = null;
                        f.mView = null;
                        // Set here to ensure that Observers are called after
                        // the Fragment's view is set to null
                        f.mViewLifecycleOwner = null;
                        f.mViewLifecycleOwnerLiveData.setValue(null);
                        f.mInnerView = null;
                        f.mInLayout = false;
                    }
                    // fall through
                case Fragment.CREATED:
                    if (newState < Fragment.CREATED) {
                        if (mDestroyed) {
                            // The fragment's containing activity is
                            // being destroyed, but this fragment is
                            // currently animating away.  Stop the
                            // animation right now -- it is not needed,
                            // and we can't wait any more on destroying
                            // the fragment.
                            if (f.getAnimatingAway() != null) {
                                View v = f.getAnimatingAway();
                                f.setAnimatingAway(null);
                                v.clearAnimation();
                            } else if (f.getAnimator() != null) {
                                Animator animator = f.getAnimator();
                                f.setAnimator(null);
                                animator.cancel();
                            }
                        }
                        if (f.getAnimatingAway() != null || f.getAnimator() != null) {
                            // We are waiting for the fragment's view to finish
                            // animating away.  Just make a note of the state
                            // the fragment now should move to once the animation
                            // is done.
                            f.setStateAfterAnimating(newState);
                            newState = Fragment.CREATED;
                        } else {
                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                            boolean beingRemoved = f.mRemoving && !f.isInBackStack();
                            if (beingRemoved || mNonConfig.shouldDestroy(f)) {
                                boolean shouldClear;
                                if (mHost instanceof ViewModelStoreOwner) {
                                    shouldClear = mNonConfig.isCleared();
                                } else if (mHost.getContext() instanceof Activity) {
                                    Activity activity = (Activity) mHost.getContext();
                                    shouldClear = !activity.isChangingConfigurations();
                                } else {
                                    shouldClear = true;
                                }
                                if (beingRemoved || shouldClear) {
                                    mNonConfig.clearNonConfigState(f);
                                }
                                f.performDestroy();
                                dispatchOnFragmentDestroyed(f, false);
                            } else {
                                f.mState = Fragment.INITIALIZING;
                            }

                            f.performDetach();
                            dispatchOnFragmentDetached(f, false);
                            if (!keepActive) {
                                if (beingRemoved || mNonConfig.shouldDestroy(f)) {
                                    makeInactive(f);
                                } else {
                                    f.mHost = null;
                                    f.mParentFragment = null;
                                    f.mFragmentManager = null;
                                    if (f.mTargetWho != null) {
                                        Fragment target = mActive.get(f.mTargetWho);
                                        if (target != null && target.getRetainInstance()) {
                                            // Only keep references to other retained Fragments
                                            // to avoid developers accessing Fragments that
                                            // are never coming back
                                            f.mTarget = target;
                                        }
                                    }
                                }
                            }
                        }
                    }
            }
        }

        if (f.mState != newState) {
            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
                    + "expected state " + newState + " found " + f.mState);
            f.mState = newState;
        }
    }

并且在把每一次的操作(add、replace、remove等)封装为一个指令存到androidx.fragment.app.FragmentTransaction.Op中来维护状态栈BackStackRecord,

2、使用步骤

  • getSupportFragmentManager()获取FragmentManager管理对象
  • 使用FragmentManager实例调用beginTransaction方法开启一个FragmentTransaction 事务
  • 通过事务实例调用add方法向FrameLayout帧布局容器添加Fragment对象实例(此时并未真正执行完添加)
public FragmentTransaction add(@NonNull Fragment fragment, @Nullable String tag)  {
		//只是添加到FragmentTransaction #ArrayList<Op> mOps里
        doAddOp(0, fragment, tag, OP_ADD);
        return this;
    }
  • 通过事务实例调用commit方法提交FragemntTransaction 事务
    本质上是执行androidx.fragment.app.BackStackRecord#commit的方法,把对应的操作存入队列,
androidx.fragment.app.BackStackRecord#commitInternal
    int commitInternal(boolean allowStateLoss) {
        if (mCommitted) throw new IllegalStateException("commit already called");
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(TAG);
            PrintWriter pw = new PrintWriter(logw);
            dump("  ", pw);
            pw.close();
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

再在主线程中执行androidx.fragment.app.FragmentManagerImpl#generateOpsForPendingActions方法(即执行androidx.fragment.app.FragmentManagerImpl.OpGenerator#generateOps接口方法)维护状态栈,最后进入**androidx.fragment.app.FragmentManagerImpl#moveToState(androidx.fragment.app.Fragment, int, int, int, boolean)**执行对应的生命周期方法。

以上是动态使用Fragement的一般步骤也是主流方式。

三、Fragment的应用

1、当Fragment嵌套Fragment时,内部Fragment接收不到onActivityResult()方法的回调

  1. 首先在嵌套子fragment直接通过**getParentFragment().startActivityForResult(intent, requestCode)**去开启新Activity

  2. 在最外层的fangment的onActivityResult(int requestCode, int resultCode, Intent data)去获取对用子fragment的应用去执行对应的逻辑处理方法

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data{
      super.onActivityResult(requestCode, resultCode, data);
     onActivityResult(requestCode, resultCode, data);
      ChildFragment cf=mFragments.get(index);
      //方法一 调用子Fragment的onActivityResult,然后子Fragment去处理自己的onActivityResult逻辑
      cf.onActivityResult(requestCode, resultCode, data);
      //方法2 直接在子fragment中提供对应的业务处理的public方法,一步到位
      cf.doSomething(data);
  }

N层嵌套也是一样的思路。

2、Can not perform this action after onSaveInstanceState

在Fragment中有时候(比如Activity中的onSaveInstance方法执行完毕之后,再次启动这个Fragment时)再次启动这个Fragment时候可能会报这个错误,原因就是因为使用commit方法提交事务,默认的allowStateLoss 是false就会进行状态检查时失败:

private void checkStateLoss() {
    if (isStateSaved()) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
}

解决方案有两种:

  1. 该事务使用commitAllowingStateLoss方法替代commit方法,但是有可能导致某次提交无效(宿主Activity)对于popBackStack方法没有对应的popBackStackAllowingStateLoss方法,所以可以在下次可见时再次提交事务

  2. 利用onActivityForResult方法/onNewIntent就可以做到事务的完整性,不会丢失事务。

3、Fragment的异常销毁、状态保持和恢复机制

3.1、Fragment的异常销毁

所谓Fragment异常销毁指不是因为用户主动退出(比如按BACK键后Activity被主动销毁)Activity导致其关联的Fragment被销毁,可能发生在以下的情况中:

  • 按Home 键返回Launcher时
  • 按菜单键回到系统后台并启动了其他应用时
  • 按电源键时
  • 屏幕方向切换时

Activity的异常销毁也会导致Fragment的异常销毁,当然可能还有其他特殊情况也会导致。

3.2、异常销毁的Fragment恢复机制

被异常销毁Fragment尝试恢复时(onSaveInstanceState方法中接到的Bundle对象不为null),会自动触发Activity和Fragment的onSaveInstanceState方法来恢复原来的状态(有一个前提条件是对应的View必须设置了Id),对于自定View的恢复还需要开发者自行去重写onSaveInstanceState去保存自定义属性的值和onRestoreInstanceState去还原。但是如果处理不好可能导致Fragment重叠异常。

因为通常FragmentActivity 会自动保持了Fragment的状态(FragmentState),重启后恢复时,View的可见状态并没有保存,但是Fragment默认的是show状态,在使用show、hide时都是通过add方式加载Fragment的,当add配合hide使Fragment的View变为GONE时(并没有被保持),页面重启时,add的Fragment会走全部的生命周期方法,创建View;而replace的非栈顶Fragment是不会走的,只有Back时才会触发,因此在使用replace加载Fragment时,页面重启后,Fragment的View还诶创建,而在使用add加载时视图是存在的并且重叠在一起。

4、利用无界面的Fragment保持Activity需要持久化的数据

当在Fragment的onCreate方法里执行 setRetainInstance(true)时,就可以让这个Fragment的实例被持久化存储。比如在屏幕旋转是,Activity被异常销毁后,如果不设置setRetainInstance(true)则Fragment也会被销毁,但是设置了setRetainInstance(true)之后这个Fragment实例就被保存起来了,Activity在重建时就可以获取到这个Fragment实例,自然可以得到里面的数据,就可以间接实现Activity数据的持久化,比如对Bitmap、AsyncTask、ProgressDialog等等的持久化就可以快速还原。

Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). If set, the fragment lifecycle will be slightly different when an activity is recreated:

/**
 * @author : Crazy.Mo
 */
public class RetainDataFragment extends Fragment {
    /**
     * 使用不带界面的Fragment为Activity保持的大数据
     */
    private Bitmap bigData;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //TODO 设置为true,可以在异常时(比如屏幕旋转是)不被销毁,而一直保持其引用,在Activity 重建时还能获取这个引用,自然里面的变量也没有被释放
        setRetainInstance(true);
    }

    public void setBigData(Bitmap data){
        this.bigData=data;
    }

    public Bitmap getBigData(){
        return this.bigData;
    }

    public static RetainDataFragment newInstance(String flag) {
        RetainDataFragment myFragment = new RetainDataFragment();
        Bundle args = new Bundle();
        args.putString("flag", flag);
        myFragment.setArguments(args);
        return myFragment;
    }
}

四、Fragment的一些工程代码

首先是BaseFragment,考虑到可能很多Fragment在周期方法执行时需要做一些共同的任务,我定了一个接受委托的角色,同遍历给View设置了OnClick事件。

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.crazymo.fragmentpro.FragmentDelegater;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

/**
 * TODO 子类Fragment
 * @author : Crazy.Mo
 */
public abstract class BaseFragment extends Fragment implements View.OnClickListener {
    private FragmentDelegater mDelegater;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        if(mDelegater!=null){
            mDelegater.onAttach(context);
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(mDelegater!=null){
            mDelegater.onCreate(savedInstanceState);
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if(mDelegater!=null){
            mDelegater.onCreateView(inflater, container, savedInstanceState);
        }
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if(mDelegater!=null){
            mDelegater.onViewCreated(view, savedInstanceState);
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if(mDelegater!=null){
            mDelegater.onActivityCreated(savedInstanceState);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        if(mDelegater!=null){
            mDelegater.onStart();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if(mDelegater!=null){
            mDelegater.onResume();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if(mDelegater!=null){
            mDelegater.onPause();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if(mDelegater!=null){
            mDelegater.onStop();
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if(mDelegater!=null){
            mDelegater.onDestroyView();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(mDelegater!=null){
            mDelegater.onDestroy();
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        if(mDelegater!=null){
            mDelegater.onDetach();
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
    }



    public void setDelegater(FragmentDelegater delegater){
        this.mDelegater=delegater;
    }

    protected void setOnClickListener(View view) {
        if (view != null) {
            view.setOnClickListener(this);
        }
    }

    /**
     *
     * @param views 不定参数传递
     */
    protected void setOnClickListener(View... views) {
        if (views != null && views.length > 0) {
            for (View view : views) {
                view.setOnClickListener(this);
            }
        }
    }
}

我这里为了解耦采用了一种“委托”的思想,定义了一个委托类(此处我是以打印各周期的执行日志,为了节省篇幅我把其他周期的委托调用省略了)

/**
 * 接收其他Fragment的委托,统一负责帮助其他Fragment打印他们的生命周期的日志
 * @author : Crazy.Mo
 */
public class FragmentDelegater extends Fragment {
    private Fragment mFragment;
    private IDelegate<Object> mDelegate;

    public FragmentDelegater(Fragment fragment,IDelegate<Object> delegate){
        super();
        this.mFragment=fragment;
        this.mDelegate=delegate;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        trackLife("onCreateView");
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        trackLife("onViewCreated");
    }
	...

    private void trackLife(final String methodName){
        if(mFragment!=null) {
            mDelegate.delegate(methodName);
        }
    }
}

为了进一步解耦, 还对“委托”要做具体事进行了抽象,定义了一个接口

public interface IDelegate<T> {
    void delegate(T... params);
}

/**
 * @author : Crazy.Mo
 */
public class DelegateImpl<T> implements IDelegate {

    @Override
    public void delegate(Object... param) {
        if(param[0]!=null && param[1]!=null) {
            Log.e("crazymo", param[0].getClass().getSimpleName() + "->" + param[1]);
        }
    }
}

小结

单纯从性能来说Fragment的切换肯定是要比Activity之间的跳转性能要高得多的,因为Activity的切换是需要跨进程调用AMS,而Fragment的切换仅仅是内部的状态切换,不过Fragment的使用起来有很多逻辑需要进行处理和优化,我想这也是单Activity架构的劣势所在,希望这篇文章能给你们一些更多的思路,比如説在持久化时,考虑下Fragment。

猜你喜欢

转载自blog.csdn.net/CrazyMo_/article/details/104281352