上一篇中,我們談論了support包下的viewpager+fragment的懶加載方式懶加載,現在我們來分析下androidx下的這種結構如何實現懶加載
依然是FragmentStatePagerAdapter
在上一篇中,我們知道Viewpage加載fragment的入口是setAdapter,而與之相關的fragment初始化函數是在populate()這個函數
,不了解上一篇的也沒關心,下面是Viewpager.setAdapter的源碼
Viewpager.setAdapter(PagerAdapter adapter)
/**
* Set a PagerAdapter that will supply views for this pager as needed.
*
* @param adapter Adapter to use
*/
public void setAdapter(@Nullable PagerAdapter adapter) {
if (mAdapter != null) {
//對adapter初始化
}
....
if (mAdapter != null) {
//初始化成員變量
if (mRestoredCurItem >= 0) {
....
} else if (!wasFirstLayout) {
//關鍵:進行生命周期
populate();
} else {
requestLayout();
}
}
// Dispatch the change to any listeners
if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
}
}
}
而在populate函數中,進行了mAdapter的幾個關鍵函數調用:
startUpdate()->instantiateItem(addNewItem)->setPrimaryItem()->finishUpdate
而FragmentStatePagerAdapter繼承至PagerAdapter(Viewpager.setAdapter(PagerAdapter adapter))
,并重寫了以上函數,所以,我們以FragmentStatePagerAdapter重寫的以上函數做關鍵分析
FragmentStatePagerAdapter
打開FragmentStatePagerAdapter,三個函數基本相同,但是發現setUserVisibleHint被廢棄了,之前分析setUserVisibleHint是Viewpager中setPrimaryItem函數調用,會設置為true,也就是frgament被當前Viewpager設置選中的時候,即我們懶加載優化方案可見的時候調用。
setPrimaryItem
@Override
@SuppressWarnings({"ReferenceEquality", "deprecation"})
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
//緩存的fragment不是當前選中的fragment時調用,即當前未選中->到選中
if (fragment != mCurrentPrimaryItem) {
...
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
//setUserVisibleHint(true)
fragment.setUserVisibleHint(true);
}
//替換緩存當前設置的fragment,和上面fragment != mCurrentPrimaryItem配置呼應
mCurrentPrimaryItem = fragment;
}
}
機智的我們,看到了setMaxLifecycle這個函數,而我們在setUserVisibleHint標記廢棄的注釋也看到了這個函數,下面是setUserVisibleHint函數的部分注釋:英文戰5渣的我,打開了有道翻譯
Set a hint to the system about whether this fragment's UI is currently visible to the user.
這個函數是fragment的UI對用戶可見是調用
由于上文,分析了fragment在viewpager中的生命周期是在finishUpdate通過事務commit提交的
@Override
public void finishUpdate(@NonNull ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
所以setUserVisibleHint是在生命周期之前調用的,它的注釋也說明了這個問題
This method may be called outside of the fragment lifecycle. and thus has no ordering guarantees with regard to fragment lifecycle method calls.
這個函數是在fragment的生命周期之外的。然后被標記為廢棄@Deprecated,并推薦我們使用
@deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)}instead
代替,這就回到了上文的 mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED)函數
,也就是,在可見的時候,setMaxLifecycle為Lifecycle.State.RESUMED,光看函數意思,表示,可見是時候,設置生命周期為
onResume,那么為什么是setMaxLifecycle,而不是setLifecycle呢?
setMaxLifecycle
再來看注釋:
Set a ceiling for the state of an active fragment in this FragmentManager.
為fragment的生命周期設置一個上限,
If fragment is already above the received state, it will be forced down to the correct state.
超過這個生命周期的將被(forced down)強制下降。
原來viewpager在androidx里是通過強制下降生命周期來控制的。
那么他是如何做到通過最大生命周期來下降生命周期的呢?接著往下看
@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
@NonNull Lifecycle.State state) {
addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
return this;
}
addOp函數new了一個Op,這個op這個是FragmentTransaction在Adnroidx新增事務的一個內部類,用于這個內部類里記錄了fragment,以及他的當前狀態進出場動畫等,有3個重載函數,重點看帶參的
//兼容以前的寫法
Op(int cmd, Fragment fragment) {
this.mCmd = cmd;
this.mFragment = fragment;
//默認初始化的時候最大狀態是onResume
this.mOldMaxState = Lifecycle.State.RESUMED;
//默認選中的時候最大狀態是onResume
this.mCurrentMaxState = Lifecycle.State.RESUMED;
}
Op(int cmd, @NonNull Fragment fragment, Lifecycle.State state) {
this.mCmd = cmd;
this.mFragment = fragment;
//fragment中的初始化狀態 Lifecycle.State mMaxState = Lifecycle.State.RESUMED;
this.mOldMaxState = fragment.mMaxState;
//賦值代簽狀態
this.mCurrentMaxState = state;
}
我們在添加的fragment的時候,會getSupportFragmentManager
getSupportFragmentManager
@NonNull
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
mFragments.getSupportFragmentManager()
@NonNull
public FragmentManager getSupportFragmentManager() {
return mHost.mFragmentManager;
}
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
我們找到FragmentManagerImpl類
FragmentManagerImpl
class FragmentManagerImpl extends FragmentManager
可見fragmentManager是通過FragmentManagerImpl 這個類去代理管理生命周期的
我們來尋找下他的事務
beginTransaction
@NonNull
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
可以看到這里new了一個BackStackRecord,而BackStackRecord繼承自FragmentTransaction ,所以,最終執行事務的是
BackStackRecord,現在深入BackStackRecord(this)
BackStackRecord
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
我們知道,最后fragment通過事務FragmentTransaction調用commit函數才能初始化,所以我們查看commit函數
@Override
public int commit() {
return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}
@Override
public void commitNow() {
disallowAddToBackStack();
mManager.execSingleAction(this, false);
}
@Override
public void commitNowAllowingStateLoss() {
disallowAddToBackStack();
mManager.execSingleAction(this, true);
}
commit實際執行了execSingleAction
int commitInternal(boolean allowStateLoss) {
...
//最后執行
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
//enqueueAction
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
...
synchronized (this) {
...
scheduleCommit();
}
}
//scheduleCommit
void scheduleCommit() {
synchronized (this) {
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
//remove再post
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
updateOnBackPressedCallbackEnabled();
}
}
}
可以看到,內部執行是通過handler來執行的
繼續尋找,發現最后執行一個performPendingDeferredStart函數,內部調用moveToState函數能看到設置的maxLifecycle身影
moveToState
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) {
// 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());
}
}
可以看到,當超過了設置的maxLifecycle生命周期,直接取賦值的生命周期,簡單粗暴,感興趣的同學,可以研究下moveToState函數,里面分發了所有fragment的生命周期事件,也就是之所以我們的onCreate,onCreateView,onResume等生命周期能監聽到回調的原因,還有怎么恢復緩存的。
所以,現在我們的懶加載在androidx下就顯得極其簡單了:
viewpager.setAdapter
FragmentStatePagerAdapter mAdapter = new FragmentStatePagerAdapter(getChildFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
@NonNull
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
};
mViewPage.setAdapter(mAdapter);
在FragmentStatePagerAdapter 函數中,設置了mBehavior==BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT則不會再走setUserVisibleHint函數,我們再看被選中(可見)setPrimaryItem函數
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
fragment.setUserVisibleHint(true);
}
這里設置了maxLifecycle== Lifecycle.State.RESUMED,這就和activity可見的生命周期相同,控制懶加載也就極其簡單
/**
* 當前的activity是否已經可見
*/
private boolean currentVisibleStatus = false;
@Override
public void onResume() {
super.onResume();
Log.i(TAG, getClass().getSimpleName() + "==>onResume");
if (currentVisibleStatus) {
dispatchVisibleStatus(false);
} else {
dispatchVisibleStatus(true);
}
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG, getClass().getSimpleName() + "==>onResume");
if (currentVisibleStatus) {
dispatchVisibleStatus(false);
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i(TAG, "==>setUserVisibleHint");
}
我們運行下看下log:
可以看到setUserVisibleHint并未執行log打印,并且在嵌套模式下以依然可以正常分發到子fragment
完整代碼
LazyFragmentx
/**
* @author chenke
* @create 2021/2/25
* @Describe AndroidX中的fragment懶加載
*/
public abstract class LazyFragmentX extends Fragment {
private final String TAG = "lazy_x";
/**
* 抽象布局
*
* @return
*/
public abstract int getLayout();
/**
* @param view 初始化view
*/
public abstract void initView(View view);
private View mRootView;
/**
* 當前的activity是否已經可見
*/
private boolean currentVisibleStatus = false;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(getLayout(), container, false);
}
initView(mRootView);
Log.i(TAG, getClass().getSimpleName() + "==>onCreateView");
return mRootView;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i(TAG, "==>setUserVisibleHint");
}
@Override
public void onStart() {
super.onStart();
Log.i(TAG, getClass().getSimpleName() + "==>onStart");
}
@Override
public void onResume() {
super.onResume();
Log.i(TAG, getClass().getSimpleName() + "==>onResume");
if (currentVisibleStatus) {
dispatchVisibleStatus(false);
} else {
dispatchVisibleStatus(true);
}
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG, getClass().getSimpleName() + "==>onResume");
if (currentVisibleStatus) {
dispatchVisibleStatus(false);
}
}
@Override
public void onStop() {
super.onStop();
Log.i(TAG, getClass().getSimpleName() + "==>onResume");
}
public void dispatchVisibleStatus(boolean visibleStatus) {
this.currentVisibleStatus = visibleStatus;
if (visibleStatus) {
onStartLoad();
} else {
onStopLoad();
}
}
public void onStartLoad() {
Log.i(TAG, getClass().getSimpleName() + "==>onStartLoad加載數據");
}
public void onStopLoad() {
Log.i(TAG, getClass().getSimpleName() + "==>onStopLoad暫停加載");
}
}
總結:
1:androidx,對于viewpager+fragment設計的界面,再創建FragmentStatePagerAdapter是調用
FragmentStatePagerAdapter(@NonNull FragmentManager fm,
@Behavior int behavior)構造函數,并且傳入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT或者1
2:Fragment的生命周期是通過主線程的handler來輪詢分發處理的
3:setMaxLifecycle的原理就是直接將超過set進去的生命周期,賦值為設置的生命周期,具體邏輯在FragmentManagerImpl.moveToState函數中
4:moveToState控制了所有的fragment的生命周期的調用分發
項目地址:
demo地址:github