Android-Fragment简要分析

2018-03-06  本文已影响0人  wyonxue

注意:文章对Fragment源码的分析基于support v4的Fragment包,版本号为25.3.1

Fragment相关类UML图

1008428-b84d45b1ba19d73d.jpg

support包中对负责管理Fragment生命是FragmentActivity,v7包的AppCompatActivity也是继承于它。
FragmentActivity管理Fragment是通过它内部mFragments的变量,mFragments类型为FragmentController.

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

FragmentActivity传递自身生命状态、状态保存、恢复都是通过调用mFragments相应的方法进行处理;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);
 
    super.onCreate(savedInstanceState);
 
    //...
    mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
 
    //...
    mFragments.dispatchCreate();
}
 
public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
}

而FragmentController本身只是简单的将相应请求转发给FragmentManagerImpl,只是起到了桥梁沟通作用。

FragmentActivity创建mFragments时传递了一个HostCallbacks内部类实例,这个callback对象给Fragment提供了获取Context,启动Activity的能力。

Fragment

Fragment主要是由Attr、View、State构成,
Attr:固有属性,Fragment基本信息,基本不会随生命周期变化。

Bundle mArguments;  //构造参数
boolean mFromLayout; //是否从layout文件中创建
String mTag;
...

View:Fragment管理View所依赖的相关成员变量。

// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
 
// The View generated for this fragment.
View mView;
 
// The real inner view that will save/restore state.
View mInnerView;

State:Fragment的状态,管理Fragment的显示、生命周期。

int mState = INITIALIZING; //生命周期状态
boolean mAdded; //是否被添加
boolean mRemoving; //是否被移除
boolean mHidden;// 是否被隐藏
boolean mDetached; //是否已经分离
 
// mState取值如下
static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.

Fragment的生命周期方法,onAttach, onCreate, onCreateView...都是在mState状态变化中进行回调的。

Fragment的事务:FragmentTransaction、BackStackRecord

添加一个Fragment,我们一般使用如下代码:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.contaniner, fragment);
transaction.commit();

FragmentTransaction表示一个事务,提供了如add、remove、attach、detach、show、hide、replace等接口操控Fragment。它的具体实现类是BackStackRecord,它的接口实现如下:

@Override
public FragmentTransaction add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}
 
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    ...
    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}
 
void addOp(Op op) {
    mOps.add(op);
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}
 
@Override
public FragmentTransaction remove(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_REMOVE;
    op.fragment = fragment;
    addOp(op);
    return this;
}
...

可以看到我们调用的add、remove等方法最后都是生成一个Op对象保存在mOps列表中,Op是Operator简写表示操作,Op的cmd属性则区分操作的类型,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;

上面操作完毕后,最后是commit事务了

@Override
public int commit() {
    return commitInternal(false);
}
 
@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}
 
@Override
public void commitNow() {
    ...
    // 直接开始执行事务
    mManager.execSingleAction(this, false);
}
 
@Override
public void commitNowAllowingStateLoss() {
    ...
    // 直接开始执行事务
    mManager.execSingleAction(this, true);
}
 
int commitInternal(boolean allowStateLoss) {
    ...
    // 事务入对象,等待下一个Handler回调执行
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

*AllowingStateLoss表示是否允许状态丢失,如果不允许但是当前Activity状态已经触发保存了调用会抛出异常;commit、commitNow的区别则是提交事务等待下个主线程Handler回调执行还是立即执行,除了执行时机不一样外,两者最后触发的事务执行逻辑是一样的。

事务执行逻辑依然在BackStackRecord中:

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

执行逻辑是遍历所有添加的操作,执行相应的FragmentManage方法,最后将Fragment的状态移动的相应的状态。

Fragment状态变迁:moveToState

Fragment添加到FragmentMange中后,要将它移动到相应的状态,使Fragment正确的显示、交互。

FragmentManage的成员变量mCurState存储着当前Activity状态对应的Fragment状态值,它的取值范围如同Fragment。Activity的状态发生变化时都会触发相应方法修改mCurState,并将所有的active 的Fragment移动到相应的状态上。

public void dispatchCreate() {
    mStateSaved = false;
    moveToState(Fragment.CREATED, false);
}
 
public void dispatchActivityCreated() {
    mStateSaved = false;
    moveToState(Fragment.ACTIVITY_CREATED, false);
}
 
public void dispatchStart() {
    mStateSaved = false;
    moveToState(Fragment.STARTED, false);
}
 
public void dispatchResume() {
    mStateSaved = false;
    moveToState(Fragment.RESUMED, false);
}
 
public void dispatchPause() {
    moveToState(Fragment.STARTED, false);
}
 
public void dispatchStop() {
    // See saveAllState() for the explanation of this.  We do this for
    // all platform versions, to keep our behavior more consistent between
    // them.
    mStateSaved = true;
 
    moveToState(Fragment.STOPPED, false);
}
 
public void dispatchReallyStop() {
    moveToState(Fragment.ACTIVITY_CREATED, false);
}
 
public void dispatchDestroyView() {
    moveToState(Fragment.CREATED, false);
}
 
public void dispatchDestroy() {
    mDestroyed = true;
    execPendingActions();
    moveToState(Fragment.INITIALIZING, false);
    mHost = null;
    mContainer = null;
    mParent = null;
}

FragmentManage的moveToState方法就负责了将自己管理的所有Fragment的状态移动到对应的状态上,当mCurState变化,或者Fragment操作触发都会调用该方法。

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    // Fragments that are not currently added will sit in the onCreate() state.
    ...
    if (f.mState < newState) {
        // For fragments that are created from a layout, when restoring from
        // state we don't want to allow them to be created until they are
        // being reloaded from the layout.
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                   ...
                }
            case Fragment.ACTIVITY_CREATED:
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                    f.performStart();
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                    f.performResume();
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null;
                }
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                    f.performPause();
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                    f.performStop();
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                   ...
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    ...
                }
        }
    }
 
    ...
}

fragment的state取值,为前面提到的七中状态,其中最低值是INITIALIZING状态,代表fragment刚创建,还未被add, 最高状态值是RESUMED,代表fragment处于前台。 所以moveToState内部分两条线,状态跃升,和状态降低,里面各有一个switch判断,注意到switch里每个case都没有break,这意味着,状态可以持续变迁,比如从INITIALIZING,一直跃升到RESUMED,将每个case都走一遍,每次case语句内,都会改变state的值。

Untitled.png

Fragment状态保存、恢复

主要实现代码是FragmentManageImpl的saveAllState()和restoreAllState()方法。

FragmentActivity的onSaveInstance()方法中会间接调用FragmentManageImpl的saveAllState()方法,而onCreate(Bundle)中会间接调用restoreAllState()。在Fragment的onCreate(Bundle)等初始化生命方法中会传入保存状态的Bundle,以供开发者恢复存储的数据。而Fragment中view的状态的恢复则是在回调了onCreateView()后得到开发者返回的view,手动触发view状态的恢复:

// FragmentManageImpl.class
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
  ...
  if(f.mState < newState) { // 状态升
     ...
    switch(f.mState) {
      ...
      case Fragment.CREATED:
        ...
        f.mView = f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
        ...
        f.performActivityCreated(f.mSavedFragmentState);
        ...
        if (f.mView != null) {
          f.restoreViewState(f.mSavedFragmentState);
        }
        f.mSavedFragmentState = null;
    }
    ...
  }
  ...
}

// Fragment.class
  final void restoreViewState(Bundle savedInstanceState) {
        if (mSavedViewState != null) {
            mInnerView.restoreHierarchyState(mSavedViewState);
            mSavedViewState = null;
        }
        mCalled = false;
        onViewStateRestored(savedInstanceState);
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onViewStateRestored()");
        }
    }

Fragment常见错误:

可以参考:https://www.jianshu.com/p/d9143a92ad94

getActivity空指针错误

状态没保存、值丢失

Can not perform this action after onSaveInstanceState异常

Fragment类必须提供无参构造方法

Fragment重叠显示异常

其他

Fragment中还有回退栈、动画、Android5.0中的共享元素及Activity动画、Fragment.retainInstance属性、LoaderManager、ChildFragmentManage等知识点这里先不讨论了。

参考
从源码角度剖析Fragment核心知识点

上一篇下一篇

猜你喜欢

热点阅读