AndroidX 下的Fragment 源码机制分析

标准的Fragment声明周期

fragment_lifecycle.png

入口分析

从FragmentActivity 开始看 onCreate 等很多分发操作,看下整个核心架构图

FragmentActivity上的所有生命周期相关API都会调用

FragmentController对应的dispatchXXX()

mFragments.dispatchCreate();

mHost.mFragmentManager.dispatchCreate();

Fragment 管理核心结构.png

FragmentManager 是通过FragmentManagerImpl 实现大量的 dispatchXXX 分发操作

核心: activitylifecycle ------> dispatchStateChange

public void dispatchCreate() {
    mStateSaved = false;
    mStopped = false;
    dispatchStateChange(Fragment.CREATED);
}

public void dispatchActivityCreated() {
    mStateSaved = false;
    mStopped = false;
    dispatchStateChange(Fragment.ACTIVITY_CREATED);
}

public void dispatchStart() {
    mStateSaved = false;
    mStopped = false;
    dispatchStateChange(Fragment.STARTED);
}

public void dispatchResume() {
    mStateSaved = false;
    mStopped = false;
    dispatchStateChange(Fragment.RESUMED);
}

public void dispatchPause() {
    dispatchStateChange(Fragment.STARTED);
}

public void dispatchStop() {
    mStopped = true;
    dispatchStateChange(Fragment.ACTIVITY_CREATED);
}

public void dispatchDestroyView() {
    dispatchStateChange(Fragment.CREATED);
}

public void dispatchDestroy() {
    mDestroyed = true;
    execPendingActions();
    dispatchStateChange(Fragment.INITIALIZING);
    mHost = null;
    mContainer = null;
    mParent = null;
}

private void dispatchStateChange(int nextState) {
    try {
        mExecutingActions = true;
        moveToState(nextState, false);
    } finally {
        mExecutingActions = false;
    }
    execPendingActions();
}

这五个状态控制了声明周期 配合dispatchxxx 方法

生命周期的调用都会走到这个方法中
dispatchStateChange(Fragment.CREATED)

static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // Fully created, not started.
static final int STARTED = 3;          // Created and started, not resumed.
static final int RESUMED = 4;          // Created started and resumed.

int mState = INITIALIZING;

通过源代码,发现核心就是这个方法管理状态

void moveToState(int newState, boolean always)

final ArrayList<Fragment> mAdded = new ArrayList<>();

moveFragmentToExpectedState(f); 加入到mAdded集合

接下来
在这个方法中去回调Fragment生命周期的方法
moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);

void moveToState(Fragment f, int newState, int transit, int transitionStyle,

初始化状态INITIALIZING

所以我们先走到f.onAttach(mHost.getContext());---> dispatchOnFragmentAttached(f, mHost.getContext(), false);

void dispatchOnFragmentCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState,
        boolean onlyRecursive) {

最后回调这里。其他声明周期整个流程类似

void performCreate(Bundle savedInstanceState)

状态赋值 CREATE

Switch 里面的 思路就是

创建声明周都是从CREATE开始向下 流

反过来下面的部分就反向流标记是 RESUMED

我们从用户的使用角度看,来分析,所有的操作有如下这些

// TODO: 2020/9/23 查看测试Fragment androidX 源代码入口
//        ft.add()
//        ft.remove();
//        ft.replace();
//        ft.hide();
//        ft.show();
        // TODO: 2020/9/23 导航相关
//        ft.setPrimaryNavigationFragment();
        // TODO: 2020/9/23 加入回退栈
//        ft.addToBackStack();

//        ft.commit()
//        ft.commitAllowingStateLoss()
//        ft.commitNow();
//        ft.commitNowAllowingStateLoss();
// TODO: 2020/9/23 END

FragmentTransaction 事务就是一组操作要么全做,要么全部做

开启事务返回BackStackRecord 每一个这个就是一个事务

add---->addOp

addOp(new Op(opcmd, fragment));

commit-> commitInternal

mManager.enqueueAction(this, allowStateLoss);

存入 ArrayList<OpGenerator> mPendingActions; 集合

void scheduleCommit()::mHost.getHandler().post(mExecCommit);

mExecCommit 任务

/**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        ensureExecReady(true);

        boolean didSomething = false;
        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
            mExecutingActions = true;
            try {
                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
            } finally {
                cleanupExec();
            }
            didSomething = true;
        }

        doPendingDeferredStart();
        burpActive();

        return didSomething;
    }/**
 * Only call from main thread!
 */
public boolean execPendingActions() {

这里的动作就是:循环判断和赋值这两个集合记录generateOpsForPendingActions

ArrayList<BackStackRecord> mTmpRecords;
ArrayList<Boolean> mTmpIsPop;

调用 BackStackRecore-> generateOps 或者 PopStackStateImpl-> generateOps

目的就是给这个 mTmpIsPop 赋值 并且吧pendingAction 集合 操作放到record,因为之前事务执行add栈操作和没有执行的进行分类

接下来会做一个裁剪操作 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop)

相当与做了优化,重复操作add栈不会执行

BackStackRecord 标记 
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;


executeOpsTogether-》expandOps
 

OP_ADD,OP_ATTACH 都是 add 操作。OP_REMOVE,OP_DETACH 都是remove操作 然后判断导航add

OP_REPLACE 判断是否是同一个id,相同不操作。

这些操作内部优化大部分变成add 操作。

优化结束开始执行操作,执行真正的【提交】

executeOps  一开是isProp false

进入switch,执行上面所有对应标记的操作

addFragment ----》 又执行到了【moveToState(fragment);】就会执行声明周期了

这样就进行合并执行的操作,整个流程,有点像状态机 之前的操作都被分解成OP_ADD

Fragment核心代码流程.png

addToBackStack 流程

commitInternal 有个判断如果mAddToBackStack

-> allocBackStackIndex

public int allocBackStackIndex(BackStackRecord bse) {
    synchronized (this) {
        if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
            if (mBackStackIndices == null) {
                mBackStackIndices = new ArrayList<BackStackRecord>();
            }
            int index = mBackStackIndices.size();
            if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
            mBackStackIndices.add(bse);
            return index;

        } else {
            int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
            if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
            mBackStackIndices.set(index, bse);
            return index;
        }
    }
}
// Must be accessed while locked.
ArrayList<BackStackRecord> mBackStackIndices;
ArrayList<Integer> mAvailBackStackIndices;
public void freeBackStackIndex(int index) {
    synchronized (this) {
        mBackStackIndices.set(index, null);
        if (mAvailBackStackIndices == null) {
            mAvailBackStackIndices = new ArrayList<Integer>();
        }
        if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
        mAvailBackStackIndices.add(index);
    }
}

操作这两个集合,有一个算法

之前是双端链表不断添加删除,现在改成两个集合算法,没有删除操作,并且空间可复用

image.png

大体算法意思,mBackStackIndices 中添加 fragment mAvailBackStackIndices 记录空闲索引值进行复用

保存状态,我们作为另一个入口分析

@Override
public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
    super.onSaveInstanceState(outState, outPersistentState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}

保存所有状态,还是用到FragmentController

Parcelable p = mFragments.saveAllState();

修改状态

mStateSaved = true;

所有状态关联上循环,

SparseArray<Fragment> mActive;

之前用的HashMap现在优化成SparseArray。 我们用的findFragmentByTag。。都保存在这个地方.

生成了N 个Fragment的所有状态属性,用来保存fragment的成员变量

FragmentState fs = new FragmentState(f);

恢复信息fs 信息会被添上

inal class FragmentState implements Parcelable {
    final String mClassName;
    final int mIndex;
    final boolean mFromLayout;
    final int mFragmentId;
    final int mContainerId;
    final String mTag;
    final boolean mRetainInstance;
    final boolean mDetached;
    final Bundle mArguments;
    final boolean mHidden;

    Bundle mSavedFragmentState;

    Fragment mInstance;
    ...
    }

fragment 树保存,

fs.mSavedFragmentState = saveFragmentBasicState(f);   这里面会保存view树
。。。
Bundle saveFragmentBasicState(Fragment f)

构建了回退栈数组 backStack = new BackStackState[N];

所有栈信息保存在FragmentManagerState

FragmentManagerState fms = new FragmentManagerState();
        fms.mActive = active;
        fms.mAdded = added;
        fms.mBackStack = backStack;
        if (mPrimaryNav != null) {
            fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
        }
        fms.mNextFragmentIndex = mNextFragmentIndex;

onSaveInstanceState 中 将信息放入Bundle

FragmentActivity -》onCreate

手机在退出或者旋转 就会调动,那么 会通过FragmentManagerImpl-->restoreAllState 恢复状态。

最终还是经过

// We need to ensure that onDestroy and any other clean up is done
                // so move the Fragment up to CREATED, then mark it as being removed, then
                // destroy it.
                moveToState(f, Fragment.CREATED, 0, 0, false);
                f.mRemoving = true;
                moveToState(f, Fragment.INITIALIZING, 0, 0, false);

状态恢复入口分析

FragmentActivity.onCreate()

Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreSaveState(p);

FragmentManagerImpl.restoreSaveState()把之前保存的数据取回来

moveToState 重新执行fragment生命周期

最后: 分析COMMIT的四种提交方式 基本核心很相似

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345