一道面試題
前段時間面試,面試官先問了一下fragment的生命周期,我一看這簡單呀,直接按照下圖回答

面試官點點頭,然后問,如果Activity里面有一個fragment,那么啟動他們時,他們的生命周期加載順序是什么?

所以今天,我們好好了解了解這個用得非常多,但是對底層不是很理解的fragment吧
首先回答面試官的問題,Fragment 的 start與activity 的start 的呼叫時機
呼叫順序:
D/MainActivity: MainActivity:
D/MainActivity: onCreate: start
D/MainFragment: onAttach:
D/MainFragment: onCreate:
D/MainActivity: onCreate: end
D/MainFragment: onCreateView:
D/MainFragment: onViewCreated:
D/MainFragment: onActivityCreated:
D/MainFragment: onViewStateRestored:
D/MainFragment: onCreateAnimation:
D/MainFragment: onCreateAnimator:
D/MainFragment: onStart:
D/MainActivity: onStart:
D/MainActivity: onResume:
D/MainFragment: onResume:
可以看到Activity 在oncreate開始時,Fragment緊接著attach,create,然后activity執行完畢onCreate方法
此后都是Fragment在執行,直到onStart方法結束
然后輪到Activity,執行onStart onResume
也就是,Activity 創建的時候,Fragment一同創建,同時Fragment優先在后臺先展示好,最后Activity帶著Fragment一起展示到前臺,
是什么?
Fragment中文翻譯為”碎片“,在手機中,每一個Activity作為一個頁面,有時候太大了,尤其是在平板的橫屏下,我們希望左半邊是一根獨立模塊,右半邊是一個獨立模塊,比如一個新聞app,左邊是標題欄,右邊是顯示內容
此時就非常適合Fragment
Fragment是內嵌入Activity中的,可以在onCreateView中加載自定義的布局,使用LayoutInflater,然后Activity持有FragmentManager對Fragment進行控制,下圖是他的代碼框架

我們的Activity一般是用AppCompatActivity,而AppCompatActivity繼承了FragmentActivity
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
也就是說Activity之所支持fragment,是因為有FragmentActivity,他內部有一個FragmentController,這個controller持有一個FragmentManager,真正做事的就是這個FragmentManager的實作類FragmentManagerImpl
整體架構
回到我們剛才的面試題,關于生命周期絕對是重中之重,但是實際上,生命周期本質只是被其他地方的方法被動呼叫而已,關鍵是Fragment自己的狀態變化了,才會回呼生命周期方法,所以我們來看看fragment的狀態轉移
static final int INITIALIZING = 0; 初始狀態,Fragment 未創建
static final int CREATED = 1; 已創建狀態,Fragment 視圖未創建
static final int ACTIVITY_CREATED = 2; 已視圖創建狀態,Fragment 不可見
static final int STARTED = 3; 可見狀態,Fragment 不處于前臺
static final int RESUMED = 4; 前臺狀態,可接受用戶互動
fragment有五個狀態,
呼叫程序如下

Fragment的狀態轉移程序主要受到宿主,事務的影響,宿主一般就是Activity,在我們剛剛的題目中,看到了Activity與Fragment的生命周期交替執行,本質上就是,Activity執行完后通知了Fragment進行狀態轉移,而Fragment執行了狀態轉移后對應的回呼了生命周期方法
下圖可以更加清晰

宿主改變Fragment狀態
那么我們不禁要問,Activity如何改變Fragment的狀態?
我們知道Activity繼承于FragmentActivity,最終是通過持有的FragmentManager來控制Fragment,我們去看看
FragmentActivity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
...
mFragments.dispatchCreate();
}
可以看到,onCreate方法中執行了mFragments.dispatchCreate();,看起來像是通知Fragment的onCreate執行,這也印證了我們開始時的周期回呼順序
D/MainActivity: MainActivity:
D/MainActivity: onCreate: start // 進入onCreate
D/MainFragment: onAttach: // 執行mFragments.dispatchCreate();
D/MainFragment: onCreate:
D/MainActivity: onCreate: end // 退出onCreate
類似的FragmentActivity在每一個生命周期方法中都做了相同的事情
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
我們進入dispatchCreate看看,
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
//內部修改了兩個狀態
public void dispatchCreate() {
mStateSaved = false;
mStopped = false;
dispatchStateChange(Fragment.CREATED);
private void dispatchStateChange(int nextState) {
try {
mExecutingActions = true;
moveToState(nextState, false);// 轉移到nextState
} finally {
mExecutingActions = false;
}
execPendingActions();
}
//一路下來會執行到
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;
}
}
...
}
可以看到上面的代碼,最終執行到 moveToState,通過判斷Fragment當前的狀態,同時newState > f.mState,避免狀態回退,然后進行狀態轉移
狀態轉移完成后就會觸發對應的生命周期回呼方法
事務管理
如果Fragment只能隨著Activity的生命周期變化而變化,那就太不靈活了,所以Android給我們提供了一個獨立的操作方案,事務
同樣由FragManager管理,具體由FragmentTransaction執行,主要是添加洗掉替換Fragment等,執行操作后,需要提交來保證生效
FragmentManager fragmentManager = ...
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setReorderingAllowed(true);
transaction.replace(R.id.fragment_container, ExampleFragment.class, null); // 替換Fragment
transaction.commit();// 這里的commit是提交的一種方法
Android給我們的幾種提交方式

FragmentTransaction是個掛名抽象類,真正的實作在BackStackState回退堆疊中,我們看下commit
@Override
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
...
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);//1
} else {
mIndex = -1;
}
// 入隊操作
mManager.enqueueAction(this, allowStateLoss);//2
return mIndex;
}
可以看到,commit的本質就是將事務提交到佇列中,這里出現了兩個陣列,注釋1處
ArrayList<BackStackRecord> mBackStackIndices;
ArrayList<Integer> mAvailBackStackIndices;
public int allocBackStackIndex(BackStackRecord bse) {
synchronized (this) {
if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
if (mBackStackIndices == null) {
mBackStackIndices = new ArrayList<BackStackRecord>();
}
int index = mBackStackIndices.size();
mBackStackIndices.add(bse);
return ind
} else {
int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
mBackStackIndices.set(index, bse);
return index;
}
}
}
mBackStackIndices陣列,每個元素是一個回退堆疊,用來記錄索引,比如說,當有五個BackStackState時,移除掉1,3兩個,就是在mBackStackIndices將對應元素置為null,然后mAvailBackStackIndices會添加這兩個回退堆疊,記錄被移除的回退堆疊
當下次commit時,就判定mAvailBackStackIndices中的索引,對應的BackStackState一定是null,直接寫到這個索引即可
而一組操作都commit到同一個佇列里面,所以要么全部完成,要么全部不做,可以保證原子性
注釋二處是一個入隊操作
public void enqueueAction(OpGenerator action, boolean allowStateLoss
synchronized (this) {
...
mPendingActions.add(action);
scheduleCommit(); // 真正的提交
}
}
public void scheduleCommit() {
synchronized (this) {
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit); // 發送請求
}
}
這里最后 mHost.getHandler()是拿到了宿主Activity的handler,使得可以在主執行緒執行,mExecCommit本身是一個執行緒
我們繼續看下這個mExecCommit
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
public boolean execPendingActions() {
ensureExecReady(true);
...
doPendingDeferredStart();
burpActive();
return didSomething;
}
void doPendingDeferredStart() {
if (mHavePendingDeferredStart) {
mHavePendingDeferredStart = false;
startPendingDeferredFragments();
}
}
void startPendingDeferredFragments() {
if (mActive == null) return;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.valueAt(i);
if (f != null) {
performPendingDeferredStart(f);
}
}
}
public void performPendingDeferredStart(Fragment f) {
if (f.mDeferStart) {
f.mDeferStart = false;
moveToState(f, mCurState, 0, 0, false); // 最終到了MoveToState
}
}
還記得我們在宿主改變Fragment狀態,里面的最終路徑嗎?是的,就是這個moveToState,無論是宿主改變Fragment狀態,還是事務來改變,最終都會執行到moveToState,然后call對應的生命周期方法來執行,這也是為什么我們要將狀態轉移作為學習主線,而不是生命周期,
除了commit,可以看到FragmentTransaction有眾多對Fragment進行增刪改查的方法

都是由BackStackState來執行,最后都會執行到moveToState中
具體是如何改變的,有很多細節,這里不再贅述,
小結
本節我們講了Fragment在android系統中的狀態,那就是通過自身狀態轉移來回呼對應生命周期方法,這塊是自動實作的,我們開發時不太需要關注狀態轉移,只要知道什么時候執行某個生命周期方法,然后再在對應方法中寫業務邏輯即可
有兩個方法可以讓Fragment狀態轉移,
- 宿主Activity生命周期內自動修改Fragment狀態,回呼Fragment的生命周期方法
- 通過手動提交事務,修改Fragment狀態,回呼Fragment的生命周期方法
面試復習筆記
這份資料我從春招開始,就會將各博客、論壇,網站上等優質的Android開發中高級面試題收集起來,然后全網尋找最優的解答方案,每一道面試題都是百分百的大廠面經真題+最優解答,包知識脈絡 + 諸多細節,
節省大家在網上搜索資料的時間來學習,也可以分享給身邊好友一起學習,






領取方式:需要在評論區評論:“免費領”即可
獲取地址:【Android技術交流】
https://jq.qq.com/?_wv=1027&k=GcxLMzGQ
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/333837.html
標籤:其他
