前言
今天在瀏覽技術大牛的公衆號文章,看到一篇文章講解了如何使用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的朋友有所幫助,如果有表述不清的地方歡迎留言討論。