真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

當你面試的時候,被問到關于Fragment的種種

前言

不知道你們都沒有自己特別的學習的方法,我是有吧所有的整理成筆記的習慣
比如今天講解的關于Fragment的我會做成筆記

成都創(chuàng)新互聯(lián)于2013年創(chuàng)立,先為扶風等服務建站,扶風等地企業(yè),進行企業(yè)商務咨詢服務。為扶風企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務解決您的所有建站問題。

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

相關內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star
順手留下GitHub鏈接,需要獲取相關面試等內(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();

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

  1. 創(chuàng)建 Fragment
  2. 獲取 FragmentManager
  3. 調(diào)用事務,添加、替換

我們一步步來了解這背后的故事。
Fragment 大家應該比較熟悉,放到最后。
先來看看 FragmentManager 。
####二丶 FragmentManager

  public abstract class FragmentManager {...}

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

2.1.定義的操作

FragmentManager 中定義的方法如下:

   //開啟一系列對 Fragments 的操作 
   public abstract FragmentTransaction beginTransaction(); 

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

   //根據(jù) ID 找到從 XML 解析出來的或者事務中添加的 Fragment 
   //首先會找添加到 FragmentManager 中的,找不到就去回退棧里找 
   public abstract Fragment findFragmentById(@IdRes int id); 

   //跟上面的類似,不同的是使用 tag 進行查找 
   public abstract Fragment findFragmentByTag(String tag); 

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

   //立即彈出回退棧中棧頂?shù)模苯訄?zhí)行哦 
   public abstract boolean popBackStackImmediate();
   ......

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

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

  • BackStackEntryFragment 后退棧中的一個元素
  • onBackStackChangedListener:后退棧變動監(jiān)聽器
  • FragmentLifecycleCallbacks: FragmentManager 中的 Fragment 生命周期監(jiān)聽

    //后退棧中的一個元素 
    public interface BackStackEntry { 
    //棧中該元素的唯一標識 
    public int getId(); //獲取 FragmentTransaction#addToBackStack(String) 設置的名稱 public String getName(); 
    
    @StringRes 
    public int getBreadCrumbTitleRes(); 
    @StringRes 
    public int getBreadCrumbShortTitleRes(); 
    public CharSequence getBreadCrumbTitle();
    public CharSequence getBreadCrumbShortTitle(); 
    }

    可以看到 BackStackEntry 的接口比較簡單,關鍵信息就是 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 生命周期的同學一定覺得很面熟,這個接口就是為我們提供一個 FragmentManager 所 有 Fragment 生命周期變化的回調(diào)。

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

2.3.實現(xiàn)類 FragmentManagerImpl

FragmentManager 定義的任務是由 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 的要求一致
當你面試的時候,被問到關于Fragment的種種
接著還有當前的狀態(tài),當前 Fragment 的起始 mParent,以及 FragmentManagermHostmContainer。

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

  public abstract class FragmentContainer { 
     @Nullable 
     public abstract View onFindViewById(@IdRes int id); 
     public abstract boolean onHasView();
   }

FragmentHostCallback 就復雜一點了,它提供了 Fragment 需要的信息,也定義了 Fragment 宿主應該做的操作:

  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 的宿主就兩種:

  1. Activity
  2. 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 定義的關鍵方法是如何實現(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ā)送任務的,so easy 嘛。其他的異步執(zhí)行也是類似,就不贅述了。

后退棧相關方法:

  ArrayList mBackStack; 
  @Override 
  public int getBackStackEntryCount() { 
     return mBackStack != null ? mBackStack.size() : 0; 
  }
  @Override 
  public BackStackEntry getBackStackEntryAt(int index) { 
     return mBackStack.get(index); 
  }

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

三丶事務

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 設置一個 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 將布局上解除 
  //當調(diào)用這個方法時,fragment 的布局已經(jīng)銷毀了 
  public abstract FragmentTransaction detach(Fragment fragment); 

  //當前面解除一個 fragment 的布局綁定后,調(diào)用這個方法可以重新綁定 
  //這將導致該 fragment 的布局重建,然后添加、展示到界面上 
  public abstract FragmentTransaction attach(Fragment fragment);

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

  mFragmentManager.beginTransaction() 
         .replace(R.id.fl_child, getChildFragment())
   // .commit() 
         .commitAllowingStateLoss();
2.2.事務的四種提交方式

事務最終的提交方法有四種:

  1. commit()
  2. commitAllowingStateLoss()
  3. commitNow()
  4. commitNowAllowingStateLoss()

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

  public abstract int commit();

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

注意:
commit() 需要在宿主 Activity 保存狀態(tài)之前調(diào)用,否則會報錯。
這是因為如果 Activity 出現(xiàn)異常需要恢復狀態(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í)行的,立即提交任務。

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

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

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

  public abstract void commitNowAllowingStateLoss();

同步執(zhí)行的 commitAllowingStateLoss() 。
OK,了解了 FragmentTransaction 定義的操作,去看看我們真正關心的、 beginTransaction()中返回的 BackStackRecord :

  @Override 
  public FragmentTransaction beginTransaction() { 
     return new BackStackRecord(this);
   }
3.3事務真正實現(xiàn)/回退棧 BackStackRecord

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

  final class BackStackRecord extends 
        FragmentTransaction implements FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

它的關鍵成員:

  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)和動畫信息的 FragmentmOps就是棧中所有的 Fragment。事務定義的方法它是如何實現(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,構造成 Op,設置狀態(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)就很簡單了,無非就是構造一個 Op,設置對應的狀態(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í)行的任務是啥?
答案就是 Handler 發(fā)送的任務 mExecCommit :
代碼多了一點省略掉了,但我們終于找到了最終的實現(xiàn):Handler 異步發(fā)到主線,調(diào)度執(zhí)行后,聚合、修改 Ops的狀態(tài),然后遍歷、修改 Fragment 棧中的 View 的狀態(tài)。

3.4.真正處理的部分

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

  /**
   * 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; //設置屬性值 
        fragment.mRemoving = true; 
     } 
  }

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

  1. 根據(jù)狀態(tài)調(diào)用對應的生命周期方法
  2. 如果是新創(chuàng)建的,就把布局添加到 ViewGroup
四丶總結

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

  • Fragment
    • 其實是對 View 的封裝,它持有 view, containerView, fragmentManager,
      childFragmentManager
      等信息
  • FragmentManager
    • 是一個抽象類,它定義了對一個 Activity/Fragment 中 添加進來的 Fragment 列表、Fragment 回退棧的操作、管理方法
    • 還定義了獲取事務對象的方法
    • 具體實現(xiàn)在 FragmentImpl
  • FragmentTransaction
    • 定義了對 Fragment 添加、替換、隱藏等操作,還有四種提交方法
    • 具體實現(xiàn)是在 BackStackRecord

Fragment 如何實現(xiàn)布局的添加替換
通過獲得當前 Activity/FragmentFragmentManager/ChildFragmentManager,進而拿到事務的實
現(xiàn)類 BackStackRecord,它將目標 Fragment 構造成 Ops(包裝Fragment和狀態(tài)信息),然后提交給FragmentManager 處理。

如果是異步提交,就通過 Handler 發(fā)送 Runnable 任務,FragmentManager 拿到任務后,先處理 Ops
狀態(tài),然后調(diào)用 moveToState() 方法根據(jù)狀態(tài)調(diào)用 Fragment 對應的生命周期方法,從而達到Fragment 的添加、布局的替換隱藏等。

下面這張圖從下往上看就是一個 Fragment 創(chuàng)建經(jīng)歷的方法:
當你面試的時候,被問到關于Fragment的種種

嵌套 Fragment的原理

也比較簡單,Fragment 內(nèi)部有一個 childFragmentManager,通過它管理子 Fragment。
在添加子 Fragment 時,把子 Fragment 的布局 add 到父 Fragment 即可

由于很多代碼太長了,敲了一下午,眼睛和手都酸了,對這樣感興趣的可以拿這份筆記自己研究,有不懂的歡迎留言
當你面試的時候,被問到關于Fragment的種種

知識匯總的PDF相關內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star
順手留下GitHub鏈接,需要獲取相關面試等內(nèi)容的可以自己去找
https://github.com/xiangjiana/Android-MS


本文名稱:當你面試的時候,被問到關于Fragment的種種
本文URL:http://weahome.cn/article/iijdje.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部