日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

android面试之fragment,当你面试的时候,被问到关于Fragment的种种

發(fā)布時間:2025/3/19 67 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android面试之fragment,当你面试的时候,被问到关于Fragment的种种 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

不知道你們都沒有自己特別的學(xué)習(xí)的方法,我是有吧所有的整理成筆記的習(xí)慣

比如今天講解的關(guān)于Fragment的我會做成筆記

由于文章有些地方代碼過于太長了繁瑣,所以部分省略掉了,敲了一下午眼睛和手脖子都酸了,至于省略的部分,對這些筆記,面試內(nèi)容感興趣的可以看筆記研究,歡迎留言

相關(guān)內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star

(順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找)

https://github.com/xiangjiana/Android-MS

一丶Fragment 的使用

實現(xiàn)很簡單,創(chuàng)建一個的布局,然后在 Activity 里點擊時替換 Fragment

mFragmentManager = getSupportFragmentManager();

mFragmentManager.beginTransaction()

.replace(R.id.fl_content, fragment)

.commitAllowingStateLoss();

代碼很簡單,核心就三步:

創(chuàng)建 Fragment

獲取 FragmentManager

調(diào)用事務(wù),添加、替換

我們一步步來了解這背后的故事。

Fragment 大家應(yīng)該比較熟悉,放到最后。

先來看看 FragmentManager 。

####二丶 FragmentManager

public abstract class FragmentManager {...}

FragmentManager 是一個抽象類,定義了一些和 Fragment 相關(guān)的操作和內(nèi)部類/接口。

2.1.定義的操作

FragmentManager 中定義的方法如下:

//開啟一系列對 Fragments 的操作

public abstract FragmentTransaction beginTransaction();

//FragmentTransaction.commit() 是異步執(zhí)行的,如果你想立即執(zhí)行,可以調(diào)用這個方法

public abstract boolean executePendingTransactions();

//根據(jù) ID 找到從 XML 解析出來的或者事務(wù)中添加的 Fragment

//首先會找添加到 FragmentManager 中的,找不到就去回退棧里找

public abstract Fragment findFragmentById(@IdRes int id);

//跟上面的類似,不同的是使用 tag 進(jìn)行查找

public abstract Fragment findFragmentByTag(String tag);

//彈出回退棧中棧頂?shù)?Fragment,異步執(zhí)行的

public abstract void popBackStack();

//立即彈出回退棧中棧頂?shù)?#xff0c;直接執(zhí)行哦

public abstract boolean popBackStackImmediate();

......

可以看到,定義的方法有很多是異步執(zhí)行的,后面看看它究竟是如何實現(xiàn)的異步。

2.2.內(nèi)部類/接口:

BackStackEntry:Fragment 后退棧中的一個元素

onBackStackChangedListener:后退棧變動監(jiān)聽器

FragmentLifecycleCallbacks: FragmentManager 中的 Fragment 生命周期監(jiān)聽

//后退棧中的一個元素

public interface BackStackEntry {

//棧中該元素的唯一標(biāo)識

public int getId(); //獲取 FragmentTransaction#addToBackStack(String) 設(shè)置的名稱 public String getName();

@StringRes

public int getBreadCrumbTitleRes();

@StringRes

public int getBreadCrumbShortTitleRes();

public CharSequence getBreadCrumbTitle();

public CharSequence getBreadCrumbShortTitle();

}

可以看到 BackStackEntry 的接口比較簡單,關(guān)鍵信息就是 ID 和 Name。

//在 Fragment 回退棧中有變化時回調(diào)

public interface OnBackStackChangedListener {

public void onBackStackChanged();

}

//FragmentManager 中的 Fragment 生命周期監(jiān)聽

public abstract static class FragmentLifecycleCallbacks {

public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}

public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}

public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}

public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}

public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v, Bundle savedInstanceState) {}

public void onFragmentStarted(FragmentManager fm, Fragment f) {}

public void onFragmentResumed(FragmentManager fm, Fragment f) {}

public void onFragmentPaused(FragmentManager fm, Fragment f) {}

public void onFragmentStopped(FragmentManager fm, Fragment f) {}

public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}

public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}

public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}

public void onFragmentDetached(FragmentManager fm, Fragment f) {}

}

}

熟悉 Fragment 生命周期的同學(xué)一定覺得很面熟,這個接口就是為我們提供一個 FragmentManager 所 有 Fragment 生命周期變化的回調(diào)。

小結(jié):

可以看到, FragmentManager 是一個抽象類,它定義了對一個 Activity/Fragment 中 添加進(jìn)來的Fragment 列表、Fragment 回退棧的操作、管理。

2.3.實現(xiàn)類 FragmentManagerImpl

FragmentManager 定義的任務(wù)是由 FragmentManagerImpl 實現(xiàn)的。

主要成員:

final class FragmentManagerImpl extends FragmentManager implements

LayoutInflaterFactory {

ArrayList mPendingActions;

Runnable[] mTmpActions;

boolean mExecutingActions;

ArrayList mActive;

ArrayList mAdded;

ArrayList mAvailIndices;

ArrayList mBackStack;

ArrayList mCreatedMenus;

// Must be accessed while locked.

ArrayList mBackStackIndices;

ArrayList mAvailBackStackIndices;

ArrayList mBackStackChangeListeners;

private CopyOnWriteArrayList> mLifecycleCallbacks;

//...

}

可以看到, FragmentManagerImpl 中定義了 添加的、活躍的。以及回退棧的列表,這和FragmentManager 的要求一致

接著還有當(dāng)前的狀態(tài),當(dāng)前 Fragment 的起始 mParent,以及 FragmentManager 的 mHost 和mContainer。

FragmentContainer 就是一個接口,定義了關(guān)于布局的兩個方法:

public abstract class FragmentContainer {

@Nullable

public abstract View onFindViewById(@IdRes int id);

public abstract boolean onHasView();

}

而 FragmentHostCallback 就復(fù)雜一點了,它提供了 Fragment 需要的信息,也定義了 Fragment 宿主應(yīng)該做的操作:

public abstract class FragmentHostCallback extends FragmentContainer {

private final Activity mActivity;

final Context mContext;

private final Handler mHandler;

final int mWindowAnimations;

final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();

//...

}

我們知道,一般來說 Fragment 的宿主就兩種:

Activity

Fragment

比如 FragmentActivity 的內(nèi)部類 HostCallbacks 就實現(xiàn)了這個抽象類:

class HostCallbacks extends FragmentHostCallback {

public HostCallbacks() {

super(FragmentActivity.this /*fragmentActivity*/);

}

//...

@Override

public LayoutInflater onGetLayoutInflater() {

return

FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.t his);

}

@Override

public FragmentActivity onGetHost() {

return FragmentActivity.this;

}

......

}

我們再看看他對 FragmentManager 定義的關(guān)鍵方法是如何實現(xiàn)的。

@Override

public FragmentTransaction beginTransaction() {

return new BackStackRecord(this);

}

beginTransaction() 返回一個新的 BackStackRecord ,我們后面介紹。前面提到了, popBackStack() 是一個異步操作,它是如何實現(xiàn)異步的呢?

@Override

public void popBackStack() {

enqueueAction(new PopBackStackState(null, -1, 0), false);

}

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {

if (!allowStateLoss) {

checkStateLoss();

}

synchronized (this) {

if (mDestroyed || mHost == null) {

throw new IllegalStateException("Activity has been destroyed");

}

if (mPendingActions == null) {

mPendingActions = new ArrayList<>();

}

mPendingActions.add(action);

scheduleCommit();

}

}

private 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);

}

}

}

可以看到,調(diào)用到最后,是調(diào)用宿主中的 Handler來發(fā)送任務(wù)的,so easy 嘛。其他的異步執(zhí)行也是類似,就不贅述了。

后退棧相關(guān)方法:

ArrayList mBackStack;

@Override

public int getBackStackEntryCount() {

return mBackStack != null ? mBackStack.size() : 0;

}

@Override

public BackStackEntry getBackStackEntryAt(int index) {

return mBackStack.get(index);

}

可以看到,開始事務(wù)和后退棧,返回/操作的都是 BackStackRecord ,我們來了解了解它是何方神圣。

三丶事務(wù)

BackStackRecord 繼承了 FragmentTransaction :

final class BackStackRecord extends FragmentTransaction implements

FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

先來看看 FragmentTransaction 。

3.1.FragmentTransaction

FragmentTransaction 定義了一系列對 Fragment 的操作方法:

//它會調(diào)用 add(int, Fragment, String),其中第一個參數(shù)傳的是 0

public abstract FragmentTransaction add(Fragment fragment, String tag);

//它會調(diào)用 add(int, Fragment, String),其中第三個參數(shù)是 null

public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);

//添加一個 Fragment 給 Activity 的最終實現(xiàn)

//第一個參數(shù)表示 Fragment 要放置的布局 id

//第二個參數(shù)表示要添加的 Fragment,【注意】一個 Fragment 只能添加一次

//第三個參數(shù)選填,可以給 Fragment 設(shè)置一個 tag,后續(xù)可以使用這個 tag 查詢它

public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment, @Nullable String tag);

//調(diào)用 replace(int, Fragment, String),第三個參數(shù)傳的是 null

public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);

//替換宿主中一個已經(jīng)存在的 fragment

//這一個方法等價于先調(diào)用 remove(), 再調(diào)用 add() public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment, @Nullable String tag);

//移除一個已經(jīng)存在的 fragment

//如果之前添加到宿主上,那它的布局也會被移除

public abstract FragmentTransaction remove(Fragment fragment);

//隱藏一個已存的 fragment

//其實就是將添加到宿主上的布局隱藏

public abstract FragmentTransaction hide(Fragment fragment);

//顯示前面隱藏的 fragment,這只適用于之前添加到宿主上的 fragment

public abstract FragmentTransaction show(Fragment fragment);

//將指定的 fragment 將布局上解除

//當(dāng)調(diào)用這個方法時,fragment 的布局已經(jīng)銷毀了

public abstract FragmentTransaction detach(Fragment fragment);

//當(dāng)前面解除一個 fragment 的布局綁定后,調(diào)用這個方法可以重新綁定

//這將導(dǎo)致該 fragment 的布局重建,然后添加、展示到界面上

public abstract FragmentTransaction attach(Fragment fragment);

對 fragment 的操作基本就這幾步,我們知道,要完成對 fragment 的操作,最后還需要提交一下:

mFragmentManager.beginTransaction()

.replace(R.id.fl_child, getChildFragment())

// .commit()

.commitAllowingStateLoss();

2.2.事務(wù)的四種提交方式

事務(wù)最終的提交方法有四種:

commit()

commitAllowingStateLoss()

commitNow()

commitNowAllowingStateLoss()

它們之間的特點及區(qū)別如下:

public abstract int commit();

commit() 在主線程中異步執(zhí)行,其實也是 Handler 拋出任務(wù),等待主線程調(diào)度執(zhí)行。

注意:

commit() 需要在宿主 Activity 保存狀態(tài)之前調(diào)用,否則會報錯。

這是因為如果 Activity 出現(xiàn)異常需要恢復(fù)狀態(tài),在保存狀態(tài)之后的 commit() 將會丟失,這和調(diào)用的初衷不符,所以會報錯。

public abstract int commitAllowingStateLoss();

commitAllowingStateLoss() 也是異步執(zhí)行,但它的不同之處在于,允許在 Activity 保存狀態(tài)之后調(diào)用,也就是說它遇到狀態(tài)丟失不會報錯。

因此我們一般在界面狀態(tài)出錯是可以接受的情況下使用它。

public abstract void commitNow();

commitNow() 是同步執(zhí)行的,立即提交任務(wù)。

前面提到 FragmentManager.executePendingTransactions() 也可以實現(xiàn)立即提交事務(wù)。但我們一般建議使用 commitNow() , 因為另外那位是一下子執(zhí)行所有待執(zhí)行的任務(wù),可能會把當(dāng)前所有的事務(wù)都一下子執(zhí)行了,這有可能有副作用。

此外,這個方法提交的事務(wù)可能不會被添加到 FragmentManger 的后退棧,因為你這樣直接提交,有可能影響其他異步執(zhí)行任務(wù)在棧中的順序。

和 commit() 一樣, commitNow() 也必須在 Activity 保存狀態(tài)前調(diào)用,否則會拋異常。

public abstract void commitNowAllowingStateLoss();

同步執(zhí)行的 commitAllowingStateLoss() 。

OK,了解了 FragmentTransaction 定義的操作,去看看我們真正關(guān)心的、 beginTransaction()中返回的 BackStackRecord :

@Override

public FragmentTransaction beginTransaction() {

return new BackStackRecord(this);

}

3.3事務(wù)真正實現(xiàn)/回退棧 BackStackRecord

BackStackRecord 既是對 Fragment 進(jìn)行操作的事務(wù)的真正實現(xiàn),也是 FragmentManager 中的回退棧的實現(xiàn):

final class BackStackRecord extends

FragmentTransaction implements FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

它的關(guān)鍵成員:

final FragmentManagerImpl mManager;

//Op 可選的狀態(tài)值

static final int OP_NULL = 0;

static final int OP_ADD = 1;

static final int OP_REPLACE = 2;

static final int OP_REMOVE = 3;

static final int OP_HIDE = 4;

static final int OP_SHOW = 5;

static final int OP_DETACH = 6;

static final int OP_ATTACH = 7;

ArrayList mOps = new ArrayList<>();

static final class Op {

int cmd; //狀態(tài)

Fragment fragment;

int enterAnim;

int exitAnim;

int popEnterAnim;

int popExitAnim;

}

int mIndex = -1;

//棧中最后一個元素的索引

}

可以看到 Op 就是添加了狀態(tài)和動畫信息的 Fragment, mOps就是棧中所有的 Fragment。事務(wù)定義的方法它是如何實現(xiàn)的呢

先看添加一個 Fragment 到布局 add() 的實現(xiàn):

@Override

public FragmentTransaction add(int containerViewId, Fragment fragment) {

doAddOp(containerViewId, fragment, null, OP_ADD);

return this;

......

}

可以看到添加一個 Fragment 到布局很簡單,概況一下就是:

修改 fragmentManager 和 ID,構(gòu)造成 Op,設(shè)置狀態(tài)信息,然后添加到列表里。

添加完了看看替換 replace 的實現(xiàn):

@Override

public FragmentTransaction replace(int containerViewId, Fragment fragment) {

return replace(containerViewId, fragment, null);

}

@Override

public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {

if (containerViewId == 0) {

throw new IllegalArgumentException("Must use non-zero containerViewId");

}

doAddOp(containerViewId, fragment, tag, OP_REPLACE);

return this;

}

太可怕了,也是調(diào)用上面剛提到的 doAddOp() ,不同之處在于第四個參數(shù)為 OP_REPLACE ,看來之前小看了這個狀態(tài)值!

再看其他方法的實現(xiàn)就很簡單了,無非就是構(gòu)造一個 Op,設(shè)置對應(yīng)的狀態(tài)值。

@Override

public FragmentTransaction remove(Fragment fragment) {

Op op = new Op();

op.cmd = OP_REMOVE;

op.fragment = fragment;

addOp(op);

return this;

}

@Override

public FragmentTransaction hide(Fragment fragment) {

Op op = new Op();

op.cmd = OP_HIDE;

op.fragment = fragment;

addOp(op);

return this;

}

@Override

public FragmentTransaction show(Fragment fragment) {

Op op = new Op();

op.cmd = OP_SHOW;

op.fragment = fragment;

addOp(op);

return this;

}

那這些狀態(tài)值的不同是什么時候起作用的呢?

別忘了我們操作 Fragment 還有最后一步,提交。

看看這兩個是怎么實現(xiàn)的:

@Override

public int commit() {

return commitInternal(false);

}

@Override

public int commitAllowingStateLoss() {

return commitInternal(true);

}

int commitInternal(boolean allowStateLoss) {

if (mCommitted) throw new IllegalStateException("commit already called");

//...

}

}

前面已經(jīng)介紹過了, FragmentManager.enqueueAction() 最終是使用 Handler 實現(xiàn)的異步執(zhí)行。

現(xiàn)在的問題是執(zhí)行的任務(wù)是啥?

答案就是 Handler 發(fā)送的任務(wù) mExecCommit :

代碼多了一點省略掉了,但我們終于找到了最終的實現(xiàn):Handler 異步發(fā)到主線,調(diào)度執(zhí)行后,聚合、修改 Ops的狀態(tài),然后遍歷、修改 Fragment 棧中的 View 的狀態(tài)。

3.4.真正處理的部分

前面主要是對 Fragment 的包裝類 Ops 進(jìn)行一些狀態(tài)修改,真正根據(jù) Ops 狀態(tài)進(jìn)行操作在這個部分:

/**

* Executes the operations contained within this transaction. The Fragment states will only

* be modified if optimizations are not allowed.

*/

void executeOps() {

final int numOps = mOps.size();

for (int opNum = 0; opNum < numOps; opNum++) {

final Op op = mOps.get(opNum);

final Fragment f = op.fragment;

f.setNextTransition(mTransition, mTransitionStyle); switch (op.cmd) {

case OP_ADD:

f.setNextAnim(op.enterAnim);

mManager.addFragment(f, false);

break;

case OP_REMOVE:

f.setNextAnim(op.exitAnim);

mManager.removeFragment(f);

break;

case OP_HIDE:

f.setNextAnim(op.exitAnim);

mManager.hideFragment(f);

break;

case OP_SHOW:

f.setNextAnim(op.enterAnim);

mManager.showFragment(f);

break;

case OP_DETACH:

f.setNextAnim(op.exitAnim);

mManager.detachFragment(f);

break;

case OP_ATTACH:

f.setNextAnim(op.enterAnim);

mManager.attachFragment(f);

break;

default:

throw new IllegalArgumentException("Unknown cmd: " + op.cmd);

}

if (!mAllowOptimization && op.cmd != OP_ADD) {

mManager.moveFragmentToExpectedState(f);

}

}

if (!mAllowOptimization) {

// Added fragments are added at the end to comply with prior behavior.mManager.moveToState(mManager.mCurState, true);

}

}

FragmentManager 對這些方法的實現(xiàn)也很簡單,修改 Fragment 的狀態(tài)值,比如remove(Fragment) :

public void removeFragment(Fragment fragment) {

if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);

final boolean inactive = !fragment.isInBackStack();

if (!fragment.mDetached || inactive) {

if (mAdded != null) {

mAdded.remove(fragment);

}

if (fragment.mHasMenu && fragment.mMenuVisible) {

mNeedMenuInvalidate = true;

}

fragment.mAdded = false; //設(shè)置屬性值

fragment.mRemoving = true;

}

}

代碼很長,先省略掉......但做的事情很簡單:

根據(jù)狀態(tài)調(diào)用對應(yīng)的生命周期方法

如果是新創(chuàng)建的,就把布局添加到 ViewGroup 中

四丶總結(jié)

OK,看完這篇文章,相信對開頭提出的問題你已經(jīng)有了答案,這里再總結(jié)一下。

Fragment、FragmentManager、FragmentTransaction 關(guān)系

Fragment

其實是對 View 的封裝,它持有 view, containerView, fragmentManager,
childFragmentManager 等信息

FragmentManager

是一個抽象類,它定義了對一個 Activity/Fragment 中 添加進(jìn)來的 Fragment 列表、Fragment 回退棧的操作、管理方法

還定義了獲取事務(wù)對象的方法

具體實現(xiàn)在 FragmentImpl 中

FragmentTransaction

定義了對 Fragment 添加、替換、隱藏等操作,還有四種提交方法

具體實現(xiàn)是在 BackStackRecord 中

Fragment 如何實現(xiàn)布局的添加替換

通過獲得當(dāng)前 Activity/Fragment 的FragmentManager/ChildFragmentManager,進(jìn)而拿到事務(wù)的實

現(xiàn)類 BackStackRecord,它將目標(biāo) Fragment 構(gòu)造成 Ops(包裝Fragment和狀態(tài)信息),然后提交給FragmentManager 處理。

如果是異步提交,就通過 Handler 發(fā)送 Runnable 任務(wù),FragmentManager 拿到任務(wù)后,先處理 Ops

狀態(tài),然后調(diào)用 moveToState() 方法根據(jù)狀態(tài)調(diào)用 Fragment 對應(yīng)的生命周期方法,從而達(dá)到Fragment 的添加、布局的替換隱藏等。

下面這張圖從下往上看就是一個 Fragment 創(chuàng)建經(jīng)歷的方法:

嵌套 Fragment的原理

也比較簡單,Fragment 內(nèi)部有一個 childFragmentManager,通過它管理子 Fragment。

在添加子 Fragment 時,把子 Fragment 的布局 add 到父 Fragment 即可

由于很多代碼太長了,敲了一下午,眼睛和手都酸了,對這樣感興趣的可以拿這份筆記自己研究,有不懂的歡迎留言

知識匯總的PDF相關(guān)內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star

(順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找)

https://github.com/xiangjiana/Android-MS

總結(jié)

以上是生活随笔為你收集整理的android面试之fragment,当你面试的时候,被问到关于Fragment的种种的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。