美文网首页
Fragment解析(2)

Fragment解析(2)

作者: 就叫汉堡吧 | 来源:发表于2022-12-05 19:43 被阅读0次
    • 概述

      在《Fragment解析(1)》中,我们分析了fragment是如何从xml中解析成fragment的,以及在这个过程中fragment的生命周期方法是如何回调的,据此,我们真正理解了一个fragment对象从创建到显示的完整逻辑,在这个基础上,本文我们从代码管理fragment的角度来看下这种方式有什么不一样的地方。

    • 引出

      supportFragmentManager.beginTransaction().add().replace().remove().commit()
      

      我们知道,通过代码的方式管理fragment需要先获取一个FragmentManager,然后调用它的beginTransaction方法返回一个FragmentTransaction对象,然后调用相关的add、replace、remove等方法,最后进行commit方法完成操作,FragmentTransaction的整个操作过程是一个链式调用的过程。

    • FragmentManager

      在Activity中获取FragmentManager中直接通过getSupportFragmentManager方法获取:

      public FragmentManager getSupportFragmentManager() {
          return mFragments.getSupportFragmentManager();
      }
      

      通过前文的分析,我们知道mFragments是FragmentController,它的FragmentManager来自于mHost,mHost是HostCallback,它的mFragmentManager就是其父类的mFragmentManager,也就是FragmentManagerImpl。

      对于Fragment中获取FragmentManager来说,如果要获取它上层Activity的manager则调用getParentFragmentManager方法获取,获取的是内部的mFragmentManager;如果其内部包含着其他子Fragment,要获取管理子Fragment的manager,则调用getChildFragmentManager方法,获取到的是mChildFragmentManager,它是在Fragment内部初始化的FragmentManagerImpl:

      FragmentManager mFragmentManager;
      
      FragmentManager mChildFragmentManager = new FragmentManagerImpl();
      
      public final FragmentManager getParentFragmentManager() {
          FragmentManager fragmentManager = mFragmentManager;
          if (fragmentManager == null) {
              throw new IllegalStateException(
                      "Fragment " + this + " not associated with a fragment manager.");
          }
          return fragmentManager;
      }
      
      final public FragmentManager getChildFragmentManager() {
         if (mHost == null) {
             throw new IllegalStateException("Fragment " + this + " has not been attached yet.");
         }
         return mChildFragmentManager;
      }
      
    • beginTransaction()

      要使用FragmentManager来操作fragment需要一个FragmentTransaction对象,这通过beginTransaction方法获取:

      public FragmentTransaction beginTransaction() {
          return new BackStackRecord(this);
      }
      

      可见后续的操作都是由BackStackRecord来完成的。我们以add方法为例看进去,它定义于FragmentTransaction中,如果是传递Class而不是fragment的实例的话,则会先调用createFragment方法创建:

      private Fragment createFragment(@NonNull Class<? extends Fragment> fragmentClass,
              @Nullable Bundle args) {
          if (mFragmentFactory == null) {
              throw new IllegalStateException("Creating a Fragment requires that this "
                      + "FragmentTransaction was built with FragmentManager.beginTransaction()");
          }
          if (mClassLoader == null) {
              throw new IllegalStateException("The FragmentManager must be attached to its"
                      + "host to create a Fragment");
          }
          Fragment fragment = mFragmentFactory.instantiate(mClassLoader, fragmentClass.getName());
          if (args != null) {
              fragment.setArguments(args);
          }
          return fragment;
      }
      

      mFragmentFactory和mClassLoader的初始化则就要到BackStackRecord的构造中去找了:

      BackStackRecord(@NonNull FragmentManager manager) {
          super(manager.getFragmentFactory(), manager.getHost() != null
                  ? manager.getHost().getContext().getClassLoader()
                  : null);
          mManager = manager;
      }
      

      FragmentManager默认使用mHostFragmentFactory:

      private FragmentFactory mHostFragmentFactory = new FragmentFactory() {
          @SuppressWarnings("deprecation")
          @NonNull
          @Override
          public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
              return getHost().instantiate(getHost().getContext(), className, null);
          }
      };
      

      instantiate在HostCallbacks的顶级父类FragmentContainer中定义:

      public Fragment instantiate(@NonNull Context context, @NonNull String className,
              @Nullable Bundle arguments) {
          return Fragment.instantiate(context, className, arguments);
      }
      
      public static Fragment instantiate(@NonNull Context context, @NonNull String fname,
              @Nullable Bundle args) {
          try {
              Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass(
                      context.getClassLoader(), fname);
              Fragment f = clazz.getConstructor().newInstance();
              if (args != null) {
                  args.setClassLoader(f.getClass().getClassLoader());
                  f.setArguments(args);
              }
              return f;
          } ...
      }
      

      FragmentFactory里会对fragment的class进行缓存,知道就行。

      然后会调用重载add方法,最终会调用doAddOp方法:

      void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
          ...
          //对fragment进行一系列标准化验证
            ...
          addOp(new Op(opcmd, fragment));
      }
      
      void addOp(Op op) {
          mOps.add(op);
          op.mEnterAnim = mEnterAnim;
          op.mExitAnim = mExitAnim;
          op.mPopEnterAnim = mPopEnterAnim;
          op.mPopExitAnim = mPopExitAnim;
      }
      

      可见,整个BackStackRecord的add操作就是实例化了fragment然后封装进一个Op对象,然后放入一个List中。

      其他的像replace、remove等操作也是一样,都是调用的doAddOp方法,只不过每种操作的Op,它们的cmd参数不同:

      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;
      static final int OP_SET_PRIMARY_NAV = 8;
      static final int OP_UNSET_PRIMARY_NAV = 9;
      static final int OP_SET_MAX_LIFECYCLE = 10;
      

      好了我们现在知道了BackStackRecord的add、replace等操作做了什么,但是我们没有看到和fragment生命周期相关的逻辑,这些都在最后的commit方法逻辑里。

    • commit操作

      我们知道,代码fragment的操作都需要执行commit才能生效,那它是怎么把mOps中的Op利用起来的呢?

      commit的实现由BackStackRecord完成,实则调用了commitInternal方法:

      int commitInternal(boolean allowStateLoss) {
          if (mCommitted) throw new IllegalStateException("commit already called");
          ...
          mCommitted = true;
            //如果放入回退栈中,则使用回退栈中的index
          if (mAddToBackStack) {
              mIndex = mManager.allocBackStackIndex();
          } else {
              mIndex = -1;
          }
          mManager.enqueueAction(this, allowStateLoss);
          return mIndex;
      }
      

      allowStateLoss参数用在enqueueAction方法中,这里就执行到了FragmentManager中:

      void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
          if (!allowStateLoss) {
              if (mHost == null) {
                  if (mDestroyed) {
                      throw new IllegalStateException("FragmentManager has been destroyed");
                  } else {
                      throw new IllegalStateException("FragmentManager has not been attached to a "
                              + "host.");
                  }
              }
              checkStateLoss();
          }
          synchronized (mPendingActions) {
              if (mHost == null) {
                  if (allowStateLoss) {
                      // This FragmentManager isn't attached, so drop the entire transaction.
                      return;
                  }
                  throw new IllegalStateException("Activity has been destroyed");
              }
              mPendingActions.add(action);
              scheduleCommit();
          }
      }
      

      可以看到,allowStateLoss为true的话则不会关心mHost是否还在,默认都是false。而且enqueueAction操作还是线程安全的。action是前面的BackStackRecord,它会被添加到mPendingActions中,然后执行scheduleCommit方法:

      void scheduleCommit() {
          synchronized (mPendingActions) {
              boolean pendingReady = mPendingActions.size() == 1;
              if (pendingReady) {
                  mHost.getHandler().removeCallbacks(mExecCommit);
                  mHost.getHandler().post(mExecCommit);
                  updateOnBackPressedCallbackEnabled();
              }
          }
      }
      

      这里可以看到,执行时要求吗PendingActions内只能有一个action,然后会执行mExeCommit:

      private Runnable mExecCommit = new Runnable() {
          @Override
          public void run() {
              execPendingActions(true);
          }
      };
      
      boolean execPendingActions(boolean allowStateLoss) {
          ...
          boolean didSomething = false;
          while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
              mExecutingActions = true;
              try {
                  removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
              } finally {
                  cleanupExec();
              }
              didSomething = true;
          }
            ...
          return didSomething;
      }
      

      generateOpsForPendingActions方法会把mPendingActions中的信息填充到mTmpRecords和mTmpIsPop中(如果是单线程操作的话mPendingActions只含一个),然后mPendingActions对于当前这个操作就没有用了:

      private boolean generateOpsForPendingActions(@NonNull ArrayList<BackStackRecord> records,
              @NonNull ArrayList<Boolean> isPop) {
          boolean didSomething = false;
          synchronized (mPendingActions) {
              if (mPendingActions.isEmpty()) {
                  return false;
              }
      
              try {
                  final int numActions = mPendingActions.size();
                  for (int i = 0; i < numActions; i++) {
                      didSomething |= mPendingActions.get(i).generateOps(records, isPop);
                  }
              } finally {
                  // Whether generateOps succeeds or not, we clear the pending actions
                  // to avoid re-processing the same set of actions a second time
                  mPendingActions.clear();
                  mHost.getHandler().removeCallbacks(mExecCommit);
              }
          }
          return didSomething;
      }
      

      这里把mPendingActions及时清空说明操作只会执行一次,而且这样做会为其他线程的操作腾出mPengdingActions的使用(因为mPengdingActions里同时只能存在一个action),可见,mPendingActions就是用来临时存放和安排要执行的action。

      因为mPengdingActions中的数据是BackStackRecord,因此generateOps方法在BackStackRecord中实现:

      public boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
              @NonNull ArrayList<Boolean> isRecordPop) {
          ...
          records.add(this);
          isRecordPop.add(false);
            //在这里将记录放入回退栈中
          if (mAddToBackStack) {
              mManager.addBackStackState(this);
          }
          return true;
      }
      

      回到execPendingActions方法中,接下来会对每一对mTmpRecords和mTmpIsPop调用removeRedundantOperationsAndExecute方法:

      private void removeRedundantOperationsAndExecute(@NonNull ArrayList<BackStackRecord> records,
              @NonNull ArrayList<Boolean> isRecordPop) {
          ...
          final int numRecords = records.size();
          int startIndex = 0;
          ...
          executeOpsTogether(records, isRecordPop, startIndex, numRecords);
            ...
      }
      

      这里其实有一个排序的逻辑,和我们要分析的东西没啥关系,这里不去关心。只要知道调用了executeOpsTogether方法即可:

      private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records,
              @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
          ...
          boolean addToBackStack = false;
          if (mTmpAddedFragments == null) {
              mTmpAddedFragments = new ArrayList<>();
          } else {
              mTmpAddedFragments.clear();
          }
          mTmpAddedFragments.addAll(mFragmentStore.getFragments());
          Fragment oldPrimaryNav = getPrimaryNavigationFragment();
          for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
              final BackStackRecord record = records.get(recordNum);
              final boolean isPop = isRecordPop.get(recordNum);
              if (!isPop) {
                  oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
              }...
              addToBackStack = addToBackStack || record.mAddToBackStack;
          }
          mTmpAddedFragments.clear();
          ...
            
          executeOps(records, isRecordPop, startIndex, endIndex);
            
            ...
          // And only then do we move all other fragments to the current state
          moveToState(mCurState, true);
            
          ...
      }
      

      expandOps方法会根据mOps里面每个Op的cmd进行操作合并,比如replace操作最终也会转成add操作,在expandOps里会找到之前的把它删除然后把新的加进去。

      然后调用executePopOps方法,该方法里会根据mOps中Op的cmd调用FragmentManager的addFragment、removeFragment等方法,最终修改的就是FragmetnStore中的mActive和mAdded集合。

      最后调用moveToState方法修改所有fragment的状态,moveToState我们就不赘述了,在《Fragment解析(1)》中分析过了,在那里会执行Fragement的生命周期方法。

    • 关于show和hide

      show和hide操作实际上会修改Fragment的mHidden和mHiddenChanged属性,在FragmetnStateManager的moveToExpectedState方法的最后会修改可见性:

      if (mFragment.mHiddenChanged) {
          if (mFragment.mView != null && mFragment.mContainer != null) {
              // Get the controller and enqueue the show/hide
              SpecialEffectsController controller = SpecialEffectsController
                      .getOrCreateController(mFragment.mContainer,
                              mFragment.getParentFragmentManager());
              if (mFragment.mHidden) {
                  controller.enqueueHide(this);
              } else {
                  controller.enqueueShow(this);
              }
          }
          if (mFragment.mFragmentManager != null) {
              mFragment.mFragmentManager.invalidateMenuForFragment(mFragment);
          }
          mFragment.mHiddenChanged = false;
          mFragment.onHiddenChanged(mFragment.mHidden);
          mFragment.mChildFragmentManager.dispatchOnHiddenChanged();
      }
      

      最终会在SpecialEffectsController的enqueueXxx方法中操作:

      void enqueueShow(@NonNull FragmentStateManager fragmentStateManager) {
          enqueue(Operation.State.VISIBLE, Operation.LifecycleImpact.NONE, fragmentStateManager);
      }
      
      void enqueueHide(@NonNull FragmentStateManager fragmentStateManager) {
          enqueue(Operation.State.GONE, Operation.LifecycleImpact.NONE, fragmentStateManager);
      }
      

      enqueue会接收一个finalState参数,比如上面的Operation.State.VISIBLE和Operation.State.GONE:

      private void enqueue(@NonNull Operation.State finalState,
              @NonNull Operation.LifecycleImpact lifecycleImpact,
              @NonNull final FragmentStateManager fragmentStateManager) {
          synchronized (mPendingOperations) {
                ...
              final FragmentStateManagerOperation operation = new FragmentStateManagerOperation(
                      finalState, lifecycleImpact, fragmentStateManager, signal);
              mPendingOperations.add(operation);
              operation.addCompletionListener(new Runnable() {
                  @Override
                  public void run() {
                      if (mPendingOperations.contains(operation)) {
                          operation.getFinalState().applyState(operation.getFragment().mView);
                      }
                  }
              });
              operation.addCompletionListener(new Runnable() {
                  @Override
                  public void run() {
                      mPendingOperations.remove(operation);
                      mRunningOperations.remove(operation);
                  }
              });
          }
      }
      

      那mPendingOperations是在什么时候被处理的呢?其实是通过SpecialEffectsController的executePendingOperations方法处理的,这个方法在很多地方都调用过,比如在mFragments.dispatchXxx系列方法之后调用,或者在executeOpsTogether方法中的moveToState方法之后调用:

      moveToState(mCurState, true);
      
      Set<SpecialEffectsController> changedControllers = collectChangedControllers(
              records, startIndex, endIndex);
      for (SpecialEffectsController controller : changedControllers) {
          controller.updateOperationDirection(isPop);
          controller.markPostponedState();
          controller.executePendingOperations();
      }
      

      applyState里面修改可见性:

      void applyState(@NonNull View view) {
          switch (this) {
              case REMOVED:
                  ViewGroup parent = (ViewGroup) view.getParent();
                  if (parent != null) {
                      parent.removeView(view);
                  }
                  break;
              case VISIBLE:
                  view.setVisibility(View.VISIBLE);
                  break;
              case GONE:
                  view.setVisibility(View.GONE);
                  break;
              case INVISIBLE:
                  view.setVisibility(View.INVISIBLE);
                  break;
          }
      }
      
    • 总结

      综上可见,不管是xml加载还是通过FragmentManager直接管理,最终操作的都是FragmentStore中mAdded和mActive集合,殊途同归,最终都是通过FragmentStateManager的moveToExpectedState方法来执行生命周期方法调用的,show和hide是通过控制Fragment添加到container中的mView的Visibility来实现显示/隐藏的。

    相关文章

      网友评论

          本文标题:Fragment解析(2)

          本文链接:https://www.haomeiwen.com/subject/cunvfdtx.html