FragmentAndroid技术知识程序员

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

2016-10-26  本文已影响1087人  豆沙包67

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

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

上一篇下一篇

猜你喜欢

热点阅读