LifeCycle在Fragment中的使用

前言

今天在瀏覽技術大牛的公衆號文章,看到一篇文章講解了如何使用LifeCycle實現懶加載的新思路,經過學習和總結寫一篇播放博客分享給大家。

原文作者博客:https://juejin.im/post/5e085dafe51d45580769a1eb
(此文章已授權鴻洋公衆號)

再爲大家推薦兩位大牛的公衆號,對於處於突破拔高期的朋友非常有幫助:
1、鴻洋大牛也是CSDN的知名博主:https://me.csdn.net/lmj623565791
在這裏插入圖片描述
2、郭霖大牛博客地址:https://me.csdn.net/sinyu890807
在這裏插入圖片描述

正文

對於懶加載的實現新思路大家可以詳細閱讀原文,我這裏只是一個學習總結的筆記。首先LifeCycle,主要是給Activity和Fragment對外暴露自己的生命週期的媒介,這樣一些跟頁面無關的邏輯就方便單獨處理,例如常見的客戶端維護的Activity堆棧,在之前我們大部分都會寫在BaseActivity的週期中,現在通過LifeCycle,我們可以直接放在Application中單獨處理。

今天我們不討論LifeCycle的用法,只探討LifeCycle對於Fragment的生命週期的影響。

Fragment問題1:Fragment創建的主要生命週期:

onAttach -> onCreate -> onCreateView -> onStart -> onResume

Fragment問題2:如何改變這個週期,例如創建的時候只執行到onCreate 或者 onStart,不會執行onResume?

如果是以前我相信我絕對答不出來。如果想要實現這個需求,我們可以通過:

FragmentTransaction.setMaxLifecycle(Fragment,  Lifecycle.State)

第一個參數是要設置的Fragment,不需要解釋;
第二個參數是一個枚舉:

  public enum State {
        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;
		...
        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }
}

其中最常用的是 CREATED,STARTED,RESUMED,如果我們設置了CREATED,你會發現Fragment創建時的生命週期變爲:

onAttach -> onCreate

如果設置的是STARTED:

onAttach -> onCreate -> onCreateView -> onStart

爲什麼setMaxLifecycle可以做到這麼神奇的操作,我們從源碼做一個簡單的分析(源碼的流程實在是太多了):

1、把操作保存到列表中

@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
            @NonNull Lifecycle.State state) {
   // 添加到列表,保存起來
   addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
   return this;
}

2、調用commit,把設置的最大生命週期綁定到Fragment中

FragmentTransaction:
void executeOps() {
	...
	mManager.setMaxLifecycle(f, op.mCurrentMaxState);
	break;
	...
}

FragmentManager:
public void setMaxLifecycle(Fragment f, Lifecycle.State state) {
		// 請注意,Lifecycle.State.CREATED爲最小值,所以不支持 DESTROYED,INITIALIZED,
        if (!state.isAtLeast(Lifecycle.State.CREATED)) {
            throw new IllegalArgumentException("Cannot set maximum Lifecycle below "
                    + Lifecycle.State.CREATED);
        }
     ...
     f.mMaxState = state;
}

這裏要注意這個判斷,Lifecycle.State的最小值是CREATED,所以不支持DESTROYED和INITIALIZED。

3、在週期分發的時候,根據MaxLifeState分發週期:

FragmentActivity:
// 取最小值,保證週期不會超出範圍
 if (f.mMaxState == Lifecycle.State.CREATED) {
        newState = Math.min(newState, Fragment.CREATED);
} else {
        newState = Math.min(newState, f.mMaxState.ordinal());
}
// 判斷是否小於最大週期
if (f.mState <= newState) {
	... 分發週期
}

Fragment問題3:如果是ViewPager,LifeCycle會有哪些影響?

以FragmentStatePagerAdapter爲例,我們首先發現FragmentStatePagerAdapter的構造方法最多支持兩個參數:

 public FragmentStatePagerAdapter(@NonNull FragmentManager fm,
            @Behavior int behavior) {
      mFragmentManager = fm;
      mBehavior = behavior;
}

其中behavior默認是BEHAVIOR_SET_USER_VISIBLE_HINT:

默認值,只爲了兼容老版本的setUserVisiableByHint()

還有一個值是:BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

從命名上看,我們可以推測,只有當前Fragment才調用Resume。

這是一個單向選擇,如果是BEHAVIOR_SET_USER_VISIBLE_HINT,那麼就會和以前一樣,還會回調setUserVisibleHint,但是這個方法已經廢棄,我覺得可能有兩種原因:

1、setUserVisibleHint的週期相對不穩定,所以爲了實現懶加載不得不增加初始化判斷;
2、onResume更符合頁面回到前臺的設定。

如果設置的BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,當前Fragment會回調onResume,被划走的Fragment會回調onPause,但是不會執行setUserVisibleHint。

具體實現我們看一下源碼:

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
     ...
     if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
         fragment.setUserVisibleHint(false);
     }
	 ...
     if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
         mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
     }
     return fragment;
}

@Override
@SuppressWarnings({"ReferenceEquality", "deprecation"})
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
      ...
      // 不是當前Fragment
      if (fragment != mCurrentPrimaryItem) {
            ...
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                ...
                // 週期只到onCreate
                mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
            } else {
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
      }
      else{
      		...
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                ...
                 // 週期只到onResume
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                fragment.setUserVisibleHint(true);
            }
      }
    }

從源碼上看都是if判斷,所以具體選擇選擇方式就要看大家怎麼選擇了。

總結

以上就是今天的筆記,希望對正在研究LifeCycle的朋友有所幫助,如果有表述不清的地方歡迎留言討論。

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