美文网首页FragmentAndroid技术知识程序员
Fragment源码中的七把利刃(下)

Fragment源码中的七把利刃(下)

作者: 豆沙包67 | 来源:发表于2016-10-26 22:40 被阅读1087次

    上篇很重要,请先阅读上篇。

    FragmentTransaction

    最常见的调用

    getSupportFragmentManager().beginTransaction().add(xxFragment, xxTag).commit();
    

    追踪FragmentManager

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

    再看BackStackRecord

    public BackStackRecord(FragmentManagerImpl manager) {
        mManager = manager;
    }
    

    每次初始化Transaction会创建一个后台堆栈来保存操作(可以不止一个操作)。但最常见的用法还是每次只进行一次操作,所以还是以单个操作为例。

    看最常用的添加操作FragmentTransaction.add

    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
        doAddOp(containerViewId, fragment, tag, OP_ADD);
        return this;
    }
    
    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;
    
        if (tag != null) {
            // tag不允许重复
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            fragment.mTag = tag;
        }
    
        if (containerViewId != 0) {
            // 容器id也不允许重复
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }
    
        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        addOp(op);
    }
    

    回头看一下Op的结构

    static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }
    

    原来是一个双端链表结构,head的prev和tail的next都是null。
    添加链表就是在尾部插入新的节点。一个Transaction只能指定一个进出动画。

    图1.Op双端链表单个操作 图1.Op双端链表两个操作
    void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }
    

    添加add操作结束后到commit方法

    public int commit() {
        return commitInternal(false);
    }
    
    public int commitAllowingStateLoss() {
        return commitInternal(true);
    }
    
    int commitInternal(boolean allowStateLoss) {
        if (mCommitted) throw new IllegalStateException("commit already called");
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(TAG);
            PrintWriter pw = new PrintWriter(logw);
            dump("  ", null, pw, null);
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }
    

    BackStackRecord本身是一个Runnable,commit等于执行其run方法,看add操作的case。

    public void run() {
        //....省略
        bumpBackStackNesting(1);//计算堆栈中操作的数量
    
        Op op = mHead;
        while (op != null) {
            int enterAnim = state != null ? 0 : op.enterAnim;
            int exitAnim = state != null ? 0 : op.exitAnim;
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;      op = op.next;
        }
    
        mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }
    

    回到FragmentManger.addFragment和moveToState,此时mManager.mCurState为Fragment.INITIALIZING,只有setRetainInstance(true)时mAddToBackStack才为true,其他的上篇已经说过不再赘述。

    至此,一个add操作已经结束了。

    接着看替换replace操作

      case OP_REPLACE: {
          Fragment f = op.fragment;
          int containerId = f.mContainerId;
          if (mManager.mAdded != null) {
              for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
                  Fragment old = mManager.mAdded.get(i);
                  if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                          "OP_REPLACE: adding=" + f + " old=" + old);
                  if (old.mContainerId == containerId) {
                      if (old == f) {
                            //新旧fragment相同,则无需再次添加
                          op.fragment = f = null;
                      } else {
                            //把旧的删除
                          if (op.removed == null) {
                              op.removed = new ArrayList<Fragment>();
                          }
                          op.removed.add(old);
                          old.mNextAnim = exitAnim;
                          if (mAddToBackStack) {
                              old.mBackStackNesting += 1;
                              if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                      + old + " to " + old.mBackStackNesting);
                          }
                          mManager.removeFragment(old, transition, transitionStyle);
                      }
                  }
              }
          }
          //添加新的
          if (f != null) {
              f.mNextAnim = enterAnim;
              mManager.addFragment(f, false);
          }
      } break;
    

    再看看hide

    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "hide: " + fragment);
        if (!fragment.mHidden) {
            fragment.mHidden = true;
            if (fragment.mView != null) {
                Animation anim = loadAnimation(fragment, transition, false,
                        transitionStyle);
                if (anim != null) {
                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
                    fragment.mView.startAnimation(anim);
                }
                //就是把里面的视图指控
                fragment.mView.setVisibility(View.GONE);
            }
            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            //调用change方法
            fragment.onHiddenChanged(true);
        }
    }
    

    show方法

    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "show: " + fragment);
        if (fragment.mHidden) {
            fragment.mHidden = false;
            if (fragment.mView != null) {
                Animation anim = loadAnimation(fragment, transition, true,
                        transitionStyle);
                if (anim != null) {
                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
                    fragment.mView.startAnimation(anim);
                }
                //show就是把view设置可见
                fragment.mView.setVisibility(View.VISIBLE);
            }
            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            //调一下方法
            fragment.onHiddenChanged(false);
        }
    }
    

    过渡动画

    动画是很令人头痛的东西。在FragmentManager.moveToState中有不小的影响

    if (f.mAnimatingAway != null) {
        // The fragment is currently being animated...  but!  Now we
        // want to move our state back up.  Give up on waiting for the
        // animation, move to whatever the final state should be once
        // the animation is done, and then we can proceed from there.
        f.mAnimatingAway = null;
        moveToState(f, f.mStateAfterAnimating, 0, 0, true);
    }
    

    这个是最开始就会判断Fragment退出时是否有动画。
    再回顾一下退出的过程

        case Fragment.ACTIVITY_CREATED:
              if (newState < Fragment.ACTIVITY_CREATED) {
                if (f.mView != null && f.mContainer != null) {
                    Animation anim = null;
                    if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
                        anim = loadAnimation(f, transit, false,
                                transitionStyle);
                    }
                    //这个创建的是Transaction中添加的动画
                    if (anim != null) {
                        final Fragment fragment = f;
                        f.mAnimatingAway = f.mView;
                        f.mStateAfterAnimating = newState;
                        final View viewToAnimate = f.mView;
                        anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                viewToAnimate, anim) {
                            @Override
                            public void onAnimationEnd(Animation animation) {
                                super.onAnimationEnd(animation);
                                if (fragment.mAnimatingAway != null) {
                                    fragment.mAnimatingAway = null;
                                    //这个mStateAfterAnimating是Fragment.INITIALIZING,动画结束之后又调用了moveToState
                                    moveToState(fragment, fragment.mStateAfterAnimating,
                                            0, 0, false);
                                }
                            }
                        });
                        f.mView.startAnimation(anim);
                    }
                    f.mContainer.removeView(f.mView);
                }
                f.mContainer = null;
                f.mView = null;
                f.mInnerView = null;
            }
    

    重新又走moveToState方法,newState为Fragment.INITIALIZING,f.mAnimatingAway != null

    if (f.mAnimatingAway != null) {
        //强制结束 
        f.mAnimatingAway = null;
        moveToState(f, f.mStateAfterAnimating, 0, 0, true);
    }
    //设置true
    if (!keepActive) {
        if (!f.mRetaining) {
            makeInactive(f);
        } else {
            //没有setReenterTransition(true),会将host置空了,注意引起no host的问题
            f.mHost = null;
            f.mParentFragment = null;
            f.mFragmentManager = null;
        }
    }
    

    最后走销毁过程。过渡动画未结束就按返回键会导致no host的异常,在support v24上已经修复过了,遇到这个问题的可以详细交流。

    LoaderManager

    主要与Fragment的生命周期绑定了,无需再额外管理,只要把任务和回调接口搞定其他都不用再担心。

    版本兼容

    实践是检验真理的唯一标准。Android系统本身的碎片话,再加上国内某些大厂深度定制,解决版本兼容问题会是繁琐且耗时的工程,需要技巧和剖析问题的耐心。

    总结

    大体过了一遍Fragment相关的知识,细枝末节的地方不可能只看一两篇文章就说弄懂,归根结底需要了解和调试源码。全篇也没有提到任何具体的问题和解决方案,只要熟悉了源码,回过头再去运用和调试就变得轻而易举。
    所以,感谢阅读。

    相关文章

      网友评论

        本文标题:Fragment源码中的七把利刃(下)

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