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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章