Fragment源码分析(一):使用刨坑

Fragment源码分析分两部分:

使用刨坑

commitXXX和popXXX

前言

为什么要分析fragment呢,以为我们平时用的很多,但是总是遇到各种莫名其妙的问题,比如pop没作用啊,添加后不能隐藏啊,以及莫名其妙的崩溃啊等等。
关于fragment 的分析,网上有很多,但看他们分析的代码都是很早以前的源码,话不多说,开始分析,首先来一段代码:

FragmentManager manager = getSupportFragmentManager();1)
FirstFragment firstFragment = new FirstFragment(R.layout.fragment_first);2)
FragmentTransaction transaction = manager.beginTransaction();3)
transaction.add(R.id.fragment_one, firstFragment, "firstFragment");4)
transaction.addToBackStack(null);5)
transaction.commitXXXXXX();6)

manager.popBackStackXXX();7

很熟悉吧,先介绍一下几个核心类:

  1. FragmentActivity:自从有了这个类,就开始通过getSupportFragmentManager来获取manager管理器,其实这个类最主要的作用就是用来实现lifecycle,绑定并监听生命周期。
  2. FragmentController:这个类,控制器嘛,就是用来控制manager的,可以理解为是一个外观模式专门用来控制manager。
  3. HostCallback继承FragmentHostcallback,他是FragmentActivity的一个内部类,主要用来创建FragmentManagerImpl,以及通过它来调用manager中的方法。
  4. FragmentManagerImpl:这个类是实现类,重要性不用多说了,fragment的管理都通过它,如各种状态的fragment的集合的管理,以及fragment各种状态的切换。
  5. BackstackRecord继承FragmentTransaction:这个类是通过FragmentManagerImpl创建出来的,专门用来记录fragment的各种操作,如add、remove、hide、show等一系列操作,注意只是单纯的记录并封装成Op对象,然后添加到mOps数列中去,最后通过commit完成最后的提交。
    在这里插入图片描述

(1)获取管理manager

这个管理类主要功能:putFragment、findFragment、popXXX以及fragment的生命周期和监听等等,是一个全局管理器,单个管理通过transaction事务中的backstackRecord记录。

public FragmentManager getSupportFragmentManager() {
//这个mFragments是FragmentController
    return mFragments.getSupportFragmentManager();
}
//然后通过控制器中的callback来拿到manager的实现类。
public FragmentManager getSupportFragmentManager() {
    return mHost.mFragmentManager;
}
//这个实现类是在HostCallbacks的父类FragmentHostcallback中创建的。HostCallbacks是FragmentActivity的一个非静态内部类。
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();

(2)创建Fragment

这个没什么好讲的,常规操作

new FirstFragment(R.layout.fragment_first)

(3)创建FragmentTransaction

开启一个事务,为什么要开启一个事务呢:首先这个transaction实现类是BackstackRecord类,fragment的设计思想跟sp的设计思想是一样的,就是有操作,先记录,然后在commit的时候,同意提交处理。
这里只做add、remove、hide、show等记录操作

FragmentTransaction transaction = manager.beginTransaction();
public FragmentTransaction beginTransaction() {
//注意这里把this,也就是将manager传了进去。因为最终对fragment的事务操作还是都在管理类中实现
    return new BackStackRecord(this); 
}

(4)添加Fragment

addFragment其实就是为了给Fragment做个记录而已,并没有真正的提交。不过这里要注意的是:
坑一、如果fragment是匿名类,或者不是pubic类,或者是非静态成员类报错;
坑二、同一个fragment中的tag属性和mContainerId和mFragmentId属性一旦赋值就不能修改,否则报错。
也就是transaction.add(R.id.fragment_one, firstFragment, “firstFragment”);这句话中的firstFragment这里add过了,只有弹出栈或者移除后才能再add,否则就会报错。
坑三、一次beginTransaction()就是一次操作事务,不论你写多少add、show、hide操作就会添加到mOps数组中,一次行执行完成

transaction.add(R.id.fragment_one, firstFragment, "firstFragment");
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,
        @Nullable String tag) {
    //不论添加传什么参数,最终都是调用doAddOp方法做记录
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}
1. 给fragment添加tag属性
2. 给fragment添加mContainerId属性和mFragmentId属性
3. 最后将fragment包装成op对象,然后添加到mOps数组中
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
    final Class<?> fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)|| (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
        throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                + " must be a public static class to be  properly recreated from instance state.");
    }
    if (tag != null) {
        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) {
        if (containerViewId == View.NO_ID) {
            throw new IllegalArgumentException("Can't add fragment " + fragment + " with tag " + tag + " to container view with no 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;
    }
    addOp(new Op(opcmd, fragment));
}
//首先封装成一个Op对象,之前版本是一个双向链表,现在已经简化成一个简单的对象了,cmd就是增删替换的操作符而已
Op(int cmd, Fragment fragment) {
    this.mCmd = cmd;
    this.mFragment = fragment;
    this.mOldMaxState = Lifecycle.State.RESUMED;
    this.mCurrentMaxState = Lifecycle.State.RESUMED;
}
//最后添加到mOps数组中,然后继续给fragment添加进出的动画属性
void addOp(Op op) {
    mOps.add(op);
    op.mEnterAnim = mEnterAnim;
    op.mExitAnim = mExitAnim;
    op.mPopEnterAnim = mPopEnterAnim;
    op.mPopExitAnim = mPopExitAnim;
}

(5)addToBackStack添加回退栈

这个是重点,详细讲解,首先看入栈和退栈的关系:

坑一、入栈只有一种:addToBackStack(@Nullable String name);入参是可以为null的,这里要注意,建议这个入参要么全是null,要么全不为null,如果一部分null一部分不为null的话,后果会不堪设想,压栈顺序乱的你想砸键盘。

坑二、压栈会做判断,当压栈了,mAddToBackStack = true;你就不能commitNowXXX了,如果你commitNowXXX了,那么mAllowAddToBackStack = false了,你就不能压栈了。

transaction.addToBackStack(null);
//注意这个是nullable,也就是可以为空,且压栈处理和commitNowXXX只能二选一
public FragmentTransaction addToBackStack(@Nullable String name) {
    if (!mAllowAddToBackStack) {
        throw new IllegalStateException("This FragmentTransaction is not allowed to be added to the back stack.");
    }
    mAddToBackStack = true;
    //这里将退栈参数赋值给mName属性,注意这个mName是一次事务的
    mName = name;
    return this;
}

(6)commitXXXXXX提交事务

提交一共有5种情况,常用的是这四种:

  1. 没有Now:,就是将执行任务放到run中去又handler执行;
  2. 有Now:就是立即执行,且不支持压栈处理,那么自然也不能使用pop来弹出栈了。
  3. 没有AllowingStateLoss:就是不允许状态丢失,否则就是允许了。
transaction.commit();
transaction.commitNow();
transaction.commitAllowingStateLoss();
transaction.commitNowAllowingStateLoss();

不允许丢失的情况

如果不允许,就会做这么一个判断,checkStateLoss();

坑位:这里要知道mStateSaved状态是当执行了onSaveInstanceState的时候赋值为true的,mStopped那就是执行onStop的时候了,所以你要是在这两个方法之后执行提交,就会报错

if (!allowStateLoss) {
    checkStateLoss();
}
//这个报错可能很多人都见过,就是不能在onSaveInstanceState的时候执行commit事务的提交操作
private void checkStateLoss() {
    if (isStateSaved()) {
        throw new IllegalStateException("Can not perform this action after onSaveInstanceState");
    }
}
//这里要知道mStateSaved状态是当执行了onSaveInstanceState的时候赋值为true的,mStopped那就是执行onStop的时候了,所以你要是在这两个方法之后执行提交,就会报错
public boolean isStateSaved() {
    return mStateSaved || mStopped;
}

(7)popBackStackXXX退栈

退栈是跟压栈对应的,如果没有写addToBackStack();方法,或者使用的是commitNow()方法,那么肯定就不能使用pop退栈了,包括返回键。popBackStack方法也有6种:
有Immediate:跟commitNow是一个道理,包装成run方法交给handler去处理;
空参:其实就是对应的addToBackStack(null);
入参int:对应的mBackStack数组的index,如果有10个,id是5,那么弹出index是5和5以后所有的元素。
入参String:对应的是addToBackStack(name);方法中的name,弹出name以及name以后所有的元素;

manager.popBackStackImmediate();
manager.popBackStackImmediate(1, FragmentManager.POP_BACK_STACK_INCLUSIVE);
manager.popBackStackImmediate("fragment_one", FragmentManager.POP_BACK_STACK_INCLUSIVE);
另外三种就是对应的popBackStack()方法了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值