Fragment

193 篇文章 9 订阅
107 篇文章 0 订阅
  1. fragment基本使用
  2. fragment使用需要注意的点
    2.1 fragment重叠问题
    2.2 fragment的onActivityResult分发的问题
  3. fragment应用
    3.1 保存大数据
    3.2 处理屏幕旋转的问题
  4. framentManager的作用
  5. fragment的事务的真正实现原理以及回退栈原理
  6. fragment的生命周期

Fragment的产生与介绍

Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全由不同的Fragment组成,更帅气的是Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment。

Fragment常见使用场景

1.单Activity+多Fragment:

  • 一个app仅有一个Activity,界面皆是Frament,Activity作为app容器使用。
  • 优点:性能高,速度最快。参考:新版知乎 、google系app

2.多模块Activity+多Fragment

  • 优点:速度快,相比较单Activity+多Fragment,更易维护

3.无视图的fragment ,保存数据用

  • activity的onSaveInstanceState(Bundle outState)能保存的数据量有限,当有大量数据要保存的时候用无视图的fragment
  • Activity异常销毁时,onSaveInstanceState能保存的数据有限,数据过大容易oom。所以我们可以在onSaveInstanceState时attach一个Fragment,并将要存储的大数据保存到这个Fragment。当Activity重建时找到这个Fragment,再从中取出那些数据

Fragment基本使用

1. 静态加载Fragment

定义Fragment的xml布局文件
自定义Fragment类,继承Fragment类或其子类,同时实现onCreate()方法,在方法中,通过inflater.inflate加载布局文件,接着返回其View
在需要加载Fragment的Activity对应布局文件中<fragment>的name属性设为全限定类名,即包名.fragment
最后在Activity调用setContentView()加载布局文件即可

<fragment
    android:id="@+id/right_fragment"
    android:name="com.xxx.xxxFragment"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    />

使用注意点:
1. 静态加载的Fragment能不能嵌套使用fragment? 可以
//作业: 尝试下 fragment嵌套 静态使用fragment
2. 静态加载Fragment一旦添加就不能在运行时删除
3. 静态fragment 在哪儿初始化的?
4. fragment一定要有一个没有参数的构造函数。
fragment 为什么需要一个没有参数的构造函数? 看源码

2. 动态加载Fragment

动态加载Fragment:

  • 获得FragmentManager对象,通过getSupportFragmentManager()
  • 获得FragmentTransaction对象,通过fm.beginTransaction()
  • 调用add()方法或者repalce()方法加载Fragment;
  • 最后调用commit()方法提交事务
public class MainActivity extends FragmentActivity {
    private String TAG  = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate()");
        setContentView(R.layout.activity_main);



        if(savedInstanceState == null){
            //1.获取fragmentManager
            FragmentManager fragmentManager = getSupportFragmentManager();
            //2.开启一个fragment事务
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            //3.向FrameLayout容器添加MainFragment,现在并未真正执行
            transaction.add(R.id.frameLayout, MainFragment.newIntance(), MainFragment.class.getName());


            // 4.提交事务,真正去执行添加动作
            transaction.commit();
        }

    }


}

Fragment使用注意点与常见异常

Fragment使用注意点

  1. Fragment的onCreateView()方法返回Fragment的UI布局,需要注意的是inflate()的第三个参数是false
  2. 如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加参数,通过Bundle getArguments()获取参数,而不建议通过为Fragment添加带参数的构造函数, 因为内存重启时Fragment会自动恢复,这时只会调用无参的构造函数。
  3. 可以在Fragment的onAttach()中通过getArguments()获得传进来的参数。如果要获取Activity对象,不建议调用getActivity(),而是在onAttach()中将Context对象强转为Activity对象
  4. commit方法一定要在Activity.onSaveInstance()之前调用
  5. FragmentManager拥有回退栈(BackStack)

Fragment常见异常:

1. getActivity == null

出现的场景:请求网络接口后退出Activity(网络延迟比较高的情况容易出现)
解决方案: 不建议fragment要使用getActivity()方法获取宿主activity,
而是在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),
保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些)

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mActivity = (Activity)context;
        Log.i("Zero", "onAttach : " + getActivity());
    }

2. Can not perform this action after onSaveInstanceState

出现的场景:在子线程commit fragment
解决方案:
1)不能在onSaveInstanceState()之后的生命周期里面commit fragment
2)不要在子线程commit fragment,因为这时Activity很有可能已经走完onSaveInstanceState()

3. fragment 重叠

出现的场景1: 屏幕旋转
原因:横竖屏切换时activity 会重新调用生命周期方法,onCreate 、onStart等
( 如果配置了android:configChanges=“orientation” 会调用onConfigurationChanged()方法)
在onCreate中 add了 fragment, 屏幕切换到横屏再切换到竖屏时 onCreate 又会执行, 所以又会重新add fragment,但是fragment 自动有一个恢复机制 ,所以系统自动恢复了一个fragment ,就有了两个fragment。
解决方案:
1). 重写onSaveInstanceState方法,使activity失去自动存储的功能。
2). onCreate中判断savedInstanceState == null才进行add fragment,
if(savedInstanceState == null){add fragment }
出现的场景2:异常销毁,即不是由于用户主动退出Activity(比如按BACK键后Activity被主动销毁)导致其被销毁,而是由于其他非正常情况导致的销毁,Activity的异常销毁会导致Fragment的异常销毁。
内存重启,比如:接了个电话,进入电话页面,此时系统内存不足,杀掉了你的activity,重新返回原来页面时activity也会重新调用生命周期方法,onCreate 、onStart等
(打开手机上的“开发者选项”中的“不保留活动”选项,模拟内存重启)

4. fragment 嵌套使用引起的异常

fragment 常见操作

add

add(@NonNull Fragment fragment, @Nullable String tag)
add(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag)

containerViewId: 指定fragment添加到那个viewgroup, findFragmentById
tag : findFragmentByTag会根据tag进行寻找

添加到回退栈

addToBackStack(@Nullable String name)
popBackStack()
popBackStack(@Nullable final String name, final int flags)
addToBackStack和popBackStack这两个方法就是分别执行入栈和出栈的操作

popBackStack() 按照栈弹出的顺序回退,比如栈中有 abcd,则出栈顺序是: d c b a
popBackStack(@Nullable final String name, final int flags) name表示这个Fragment上面的Fragment都出栈,flags为1表示name指定的Fragment也出栈。
比如栈中有 abcd,
如果调用popBackStack(b,0),则栈中有 ab
如果调用popBackStack(b,1),则栈中有 a

fragment嵌套时的回退栈问题 (单activity + fragment 架构的app比较常见)

解决方案:
方案1.自定义OnBackPressed 接口,所有的Fragment都实现该接口:
interface OnBackPressed{
fun onBackPressed()
}
open class LifeCycleFragment : Fragment(), OnBackPressed

然后在activity 的onBackPressed()中管理回退栈
override fun onBackPressed() {
super.onBackPressed()
//自己管理回退栈
}
该方案比较麻烦,推荐使用方案2.
方案2.使用jetpack navigation管理fragment

多次add fragment 到同一个位置,重叠问题

调用hide隐藏上一个添加的

add 与 replace的区别

add 是往containerViewId的容器里面添加fragment view
replace将containerViewId的容器里面之前添加的view全部清空
add和replace千万不要混合使用,要么全部用add,要么全部用replace

remove 操作

remove前要判断fragment是否已经添加:fragment.isAdded

 if (fragment != null && fragment.isAdded) {
 }

setRetainInstance(true)的使用

setRetainInstance的应用

  1. Activity如何保存大数据
  2. Android屏幕旋转如何处理AsysncTask和ProgressDialog

FragmentPagerAdapter与FragmentStatePagerAdapter

FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图, 并不会销毁fragment实例。
FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底 的将Fragment从当前Activity的FragmentManager中移除,state标明,销毁时,会将其 onSaveInstanceState(Bundle outState) 中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的 fragment,也就是说,你可以在 onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行 恢复创建。

FragmentStatePagerAdapter FragmentPagerAdapter
省内存 不省
创建需要时间 不要重新创建
tab页面多 tab <= 4
销毁 detach

Fragment的生命周期

Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:
在这里插入图片描述
onAttach(Activity)
当Fragment与Activity发生关联时调用,即Fragment attached到Activity时调用。
此时Fragment是ATTACHED状态,在此之前Fragment是INITIALIZING状态。

//Fragment.java

    void performAttach() {
        ...
        
        mState = ATTACHED;
        mCalled = false;
        onAttach(mHost.getContext());
        
        ...
    }

    /**
     * Called when a fragment is first attached to its context.
     * {@link #onCreate(Bundle)} will be called after this.
     */
    @CallSuper
    public void onAttach(@NonNull Context context) {
        mCalled = true;
        final Activity hostActivity = mHost == null ? null : mHost.getActivity();
        if (hostActivity != null) {
            mCalled = false;
            onAttach(hostActivity);
        }
    }

onCreate(Bundle savedInstanceState)
该方法调用时可能Fragment所在的Activity正在Create过程中,因此不能在该方法中进行与Activity的ui视图结构的交互的相关操作。
此时Fragment是CREATED状态。

//Fragment.java

    void performCreate(Bundle savedInstanceState) {
        mChildFragmentManager.noteStateNotSaved();
        mState = CREATED;
        mCalled = false;
        mSavedStateRegistryController.performRestore(savedInstanceState);
        onCreate(savedInstanceState);
        mIsCreated = true;
        
        ...
        
    }

onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图,google官方建议只在该方法中创建布局视图,操作Fragment的布局视图的相关操作在onViewCreated()中进行。
此时Fragment是CREATED状态。

onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用,此时Fragment所在的Activity已经完成Create过程。
当执行onActivityCreated()的时候 activity的 onCreate才刚完成,所以在onActivityCreated()调用之前 activity的onCreate可能还没有完成,所以不能再onCreateView()中进行与activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面进行。
此时Fragment是ACTIVITY_CREATED状态。

onDestroyView()
与onCreateView相对应,当该Fragment的视图被移除时调用

onDestroy()
与onCreate相对应。

//Fragment.java

    /**
     * Called when the fragment is no longer in use.  This is called
     * after {@link #onStop()} and before {@link #onDetach()}.
     */
    @CallSuper
    public void onDestroy() {
        mCalled = true;
    }

onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用

//Fragment.java

    /**
     * Called when the fragment is no longer attached to its activity.  This
     * is called after {@link #onDestroy()}.
     */
    @CallSuper
    public void onDetach() {
        mCalled = true;
    }

还有一些其他回调方法你也应当用来去处理fragment生命周期的各种阶段。
首先第一次创建Fragment的时候,其实执行了这么多方法:
onAttach -> onCreate -> onCreateView ->onViewCreated -> onActivityCreated -> onViewStateRestored -> onStart -> onResume
举几个例子:
1.按HOME键或者打开别的应用时:onPause -> onStop
2.从桌面回来或者从别的应用回来:onStart -> onResume
3.离开Activity时Fragment被彻底销毁执行的是:onPause -> onStop -> onDestoryView -> onDestory -> onDetach

Fragment中定义的各个状态:

//Fragment.java

    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;

不同版本的Fragment定义的状态有不同,新版本是这几个状态:

//Fragment.java

    static final int INITIALIZING = -1;    // Not yet attached.
    static final int ATTACHED = 0;         // Attached to the host.
    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;

	...
	

Fragment的状态转换图:
在这里插入图片描述
Activity的状态与Fragment的生命周期方法回调图:
在这里插入图片描述

fragment的状态转换与生命周期方法回调图:
在这里插入图片描述
fragment的state 与Activity 的state不是等同。

与activity的生命周期详细对比

Activity的生命周期官方文档图:
在这里插入图片描述

Fragment的生命周期官方文档图:
在这里插入图片描述

生命周期探究

onHiddenChanged 的回调时机

  1. 当使用add()+show(),hide()跳转新的Fragment时,旧的Fragment回调onHiddenChanged(),不会回调onStop()等生命周期方法
  2. 而新的Fragment在创建时是不会回调onHiddenChanged()

获取FragmentManage的方式

  • getFragmentManager()
  • v4中,getSupportFragmentManager

主要的操作都是FragmentTransaction的方法

  • FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
  • transaction.add()
    往Activity中添加一个Fragment
  • transaction.remove()
    从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细
    说),这个Fragment实例将会被销毁。
  • transaction.replace()
    使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~ 5. transaction.hide()
    隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
  • transaction.show()
    显示之前隐藏的Fragment
  • transaction.detach()
    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
  • transaction.attach()
    重建view视图,附加到UI上并显示。
  • transatcion.commit()//提交一个事务
    注意:常用Fragment的哥们,可能会经常遇到Activity状态不一致:State loss这样的错误。
    主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用

上述,基本是操作Fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。

值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。

  1. 比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望回到FragmentA还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。

  2. 再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。

  3. remove 和 detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。

Fragment Arguments

setArguments方法必须在fragment创建以后,添加给Activity前完成

Fragment与Activity通信

fragment交互后数据应该通过接口返回给activity

  1. 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
  2. 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
  3. 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

Fragment与ActionBar和MenuItem集成

Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。

  1. 在Fragment的onCreate中调用 setHasOptionsMenu(true);
  2. 然后在Fragment子类中实现onCreateOptionsMenu
  3. 如果希望在Fragment中处理MenuItem的点击,也可以实现onOptionsItemSelected;当然了
    Activity也可以直接处理该MenuItem的点击事件。

Fragment原理

Fragment本质上是 view 的容器和控制器,Fragment 是 activity 的碎片。

activity 是四大组件之一,是 android 系统的组成部件,fragment 除了不是系统组件外,拥有Activity 的所有的功能。fragment 的存在就是对 activity 的功能进行拆分,降低 activity 的负担,减少 activity 中的代码量。

Fragment 可以使你能够将 activity 分离成多个可重用的组件,每个都有它自己的生命周期和UI。

Fragment 是一个独立的模块,紧紧地与 activity 绑定在一起。可以运行中动态地移除、加入、交换等。

Fragment源码分析

FragmentTransaction的add() 、replace()、 hide() 、show() -> commit ()
简单API后面是什么原理?

FragmentActivity: AMS控制Activity的生命周期,FragmentActivity在一系列的dispatchXXX方法里面调用FragmentController来控制Fragment的生命周期

FragmentController:是一个代理,所有操作都交给FragmentHostCallback实现

FragmentHostCallback: 被FragmentController持有,FragmentHostCallback有一个FragmentManagerImpl成员属性,FragmentManagerImpl真正的进行fragment操作

FragmentContainer: 定义了所在容器的Callbacks

FragmentTransaction: Fragment操作的事务,每个FragmentTransaction对象,代表着一个包含一系列Fragment操作的事务,保证一些列Fragment操作的原子性。
操作Fragment的添加,删除,隐藏,显示时,都要使用FragmentTransaction。
FragmentTransaction其实是个抽象类,真正的实现类为BackStackRecord

BackStackRecord: BackStackRecord实现了FragmentTransaction,每一个BackStackRecord对象,都代表了一个包含一系列Fragment操作的事务。

Op: Op对象,主要保存了FragmentTransaction操作事务中的单次操作,是相对于Fragment而言的。每个Op对象,保存了操作的命令、操作的Fragment对象,切换动画等对象。
比如:add()操作会new一个Op对象,把操作的命令 OP_ADD和fragment进行映射。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

创建FragmentManagerImpl对象

在FragmentActivity 创建的时候就会创建FragmentManagerImpl对象。

public class FragmentActivity extends ComponentActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
    private static final String TAG = "FragmentActivity";

	...

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

	
	//FragmentActivity.HostCallbacks继承FragmentHostCallback,通过重写FragmentHostCallback中的函数,
	//将Fragment操作与Fragment宿主(例如FragmentActivity)捆绑起来。有些方法需要通过宿主来实现,单单依靠Fragment,无法实现
	class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
	        ViewModelStoreOwner,
	        OnBackPressedDispatcherOwner {
	    public HostCallbacks() {
	        super(FragmentActivity.this /*fragmentActivity*/);
	    }
	    
		...
		
	}

}






public abstract class FragmentHostCallback<E> extends FragmentContainer {
    @Nullable private final Activity mActivity;
    @NonNull private final Context mContext;
    @NonNull private final Handler mHandler;
    private final int mWindowAnimations;
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();


	...

}
	

add、remove、replace、show、commit等过程分析

事务操作
操作	     	command	  			意义
add				OP_ADD				添加fragment
remove	    	OP_REMOVE			删除fragment
replace			OP_REPLACE			替换fragment
show			OP_SHOW				显示fragment
hide			OP_HIDE				隐藏fragment
attach			OP_ATTACH			依附activity
detach			OP_DETACH			从activity分离



cmd				firstOutFragments		lastInFragements
OP_ADD			-						被添加的fragment
OP_REPLACE		被替换的fragment			新的fragment
OP_REMOVE		被删除的fragment			-
OP_HIDE			被隐藏的fragment			-
OP_SHOW			-						被显示的fragment	
OP_DETACH		被detach的fragment		-
OP_ATTACH		-						被attach的fragment 
 


BackStackRecord的executeOps()方法
cmd				调用FragmentManager方法
OP_ADD			addFragment
OP_REPLACE		removeFragment->addFragment
OP_REMOVE		removeFragment
OP_HIDE			hideFragment
OP_SHOW			showFragment
OP_DETACH		detachFragment
OP_ATTACH		attachFragment 
 

add过程分析

//BackStackRecord.java

    @Override
    public FragmentTransaction add(int containerViewId, Fragment fragment) {
        doAddOp(containerViewId, fragment, null, OP_ADD);
        return this;
    }

    private void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
      
      ...
      
        addOp(new Op(opcmd, fragment));
    }

    void addOp(Op op) {
        mOps.add(op);//    ArrayList<Op> mOps = new ArrayList<>();
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
    }

添加 Fragment 就是给 fragment 事务对象中的Op对象链表中添加一个Op对象。

commit过程分析

commit与commitNow()与什么区别:

//BackStackRecord.java


    @Override
    public int commit() {
        return commitInternal(false);
    }

    @Override
    public int commitAllowingStateLoss() {
        return commitInternal(true);
    }

    @Override
    public void commitNow() {
        disallowAddToBackStack();
        mManager.execSingleAction(this, false);
    }

    @Override
    public void commitNowAllowingStateLoss() {
        disallowAddToBackStack();
        mManager.execSingleAction(this, 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);
            pw.close();
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);//更新 index 信息
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);//异步任务入队
        return mIndex;
    }
//FragmentManager.java


    /**
     * Adds an action to the queue of pending actions.
     *
     * @param action the action to add
     * @param allowStateLoss whether to allow loss of state information
     * @throws IllegalStateException if the activity has been destroyed
     */
    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<>();
            }
			// 将commit处理操作放置到mPendingActions列表中
            mPendingActions.add(action);
            scheduleCommit();//发送任务
        }
    }

    private void scheduleCommit() {
        synchronized (this) {
            boolean postponeReady =
                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }

    Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };

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

        boolean didSomething = false;
		//将mPendingActions列表的action拷贝到mTempActions数组里
		//mTmpRecords, mTmpIsPop 前者用来临时存储所有待执行的动作(mPendingActions)生成的 BackStackRecord,
		//后者用来存储 BackStackRecord 是否为出栈
        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
            mExecutingActions = true;
            try {
                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);//这里是入口执行 BackStackRecord 列表
            } finally {
                cleanupExec();
            }
            didSomething = true;
        }

        doPendingDeferredStart();
        burpActive();

        return didSomething;
    }


    public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
        if (allowStateLoss && (mHost == null || mDestroyed)) {
            // This FragmentManager isn't attached, so drop the entire transaction.
            return;
        }
        ensureExecReady(allowStateLoss);
        if (action.generateOps(mTmpRecords, mTmpIsPop)) {
            mExecutingActions = true;
            try {
                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
            } finally {
                cleanupExec();
            }
        }

        doPendingDeferredStart();
        burpActive();
    }

上述源码分析可以知道,commitNow()是直接同步进行Fragment的相关操作的,而commit()是把操作加入到ArrayList<OpGenerator> mPendingActions;列表中,然后调用mHost.getHandler().post(mExecCommit);异步进行操作的(消息队列里面可能还有很多其他待处理的任务,所以commit()操作不一定马上执行)。

commit与commitAllowingStateLoss有什么区别?

//FragmentManager.java


	public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
	    if (!allowStateLoss) {
	        checkStateLoss();
	    }
	        
		...
	}


    private void checkStateLoss() {
        if (isStateSaved()) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
        if (mNoTransactionsBecause != null) {
            throw new IllegalStateException(
                    "Can not perform this action inside of " + mNoTransactionsBecause);
        }
    }

    @Override
    public boolean isStateSaved() {
        // See saveAllState() for the explanation of this.  We do this for
        // all platform versions, to keep our behavior more consistent between
        // them.
        return mStateSaved || mStopped;
    }

commit()会调用checkStateLoss()判断 Activity 的状态是否已经保存,如果已经保存则报错。
即commit()需要在宿主 Activity 保存状态之前((Activity#onSaveInstanceState()被调用之前))调用,否则会报错。

而commitAllowingStateLoss可以在宿主 Activity 保存状态之后调用。

总结:
commit() 是异步执行,且需要在宿主 Activity 保存状态之前调用,否则会报错。
这是因为如果 Activity 出现异常需要恢复状态,在保存状态之后的 commit() 将会丢失,这和调用的初衷不符,所以会报错。

commitAllowingStateLoss() 也是异步执行,但它的不同之处在于,允许在 Activity 保存状态之后调用,也就是说它遇到状态丢失不会报错。
因此我们一般在界面状态出错是可以接受的情况下使用它。

commitNow() 是同步执行的,立即提交任务。和 commit() 一样, commitNow() 也必须在 Activity 保存状态前调用,否则会抛异常。

commitNowAllowingStateLoss() 同步执行的 commitAllowingStateLoss() 。

commitNow()和commitNowAllowingStateLoss() 都是同步执行的,所以都不支持添加到回退栈的操作,即:调用commitNow()或者commitNowAllowingStateLoss()之前不能调用addToBackStack()。
不能对要加在back stack中的transaction使用commitNow(), 即addToBackStack()和commitNow()不能同时使用。为什么呢?
如果你有一个提交使用了commit(), 紧接着又有另一个提交使用了commitNow(), 两个都想加入back stack, 那back stack会变成什么样呢? 到底是哪个transaction在上, 哪个在下? 答案将是一种不确定的状态, 因为系统并没有提供任何保证来确保顺序, 所以系统决定干脆不支持这个操作。

Fragment生命周期的调用

FragmentActivity的各个生命周期方法里面调用mFragments.dispatchXXXXX方法进行调用Fragment的生命周期方法。

//FragmentActivity.java

    /**
     * Perform initialization of all fragments.
     */
    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        ...
    	
        mFragments.dispatchCreate();
    }

    /**
     * Dispatch onResume() to fragments.  Note that for better inter-operation
     * with older versions of the platform, at the point of this call the
     * fragments attached to the activity are <em>not</em> resumed.
     */
    @Override
    protected void onResume() {
        super.onResume();
        mResumed = true;
        mFragments.noteStateNotSaved();
        mFragments.execPendingActions();
    }
    
    /**
     * Dispatch onStart() to all fragments.
     */
    @Override
    protected void onStart() {
        super.onStart();
		...
        mFragments.dispatchStart();
    }
//FragmentManagerImpl.java

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

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

最终调用moveToState进行Fragment状态的切换,Fragment生命周期的相关方法都在moveToState方法里面被调用。

FragmentManager的moveToState()方法
状态表格
值		状态				意义										入口方法
0		INITIALIZING		Fragment尚未初始化或者已经被销毁			dispatchDestroy
1		CREATED				Fragment已被创建							dispatchDestoryView
2		ACTIVITY_CREATED	Fragment所在Activity已经创建成功			dispatchActivityCreated
3		STARTED				Fragment已经可见,但是还不可以接收点击事件	dispatchPause
4		RESUMED				Fragment已经可见,并且可以接收点击事件		dispatchResume 
已经废弃
3		STOPPED				Fragment已经被创建,但是尚未不可见			dispatchStop 


STARTED 与 RESUMED区别:
STARTED(可见):Fragment已经可见,但是还不可以接收点击事件
RESUMED(可见且可交互):Fragment已经可见,并且可以接收点击事件
注意:可见与可交互的区别

当activity的onCreate()调用dispatchCreate()的时候,不管fragments 之前处于什么状态都会切换到CREATED状态。

在这里插入图片描述

在这里插入图片描述

Fragment如何显示到页面上

Fragment如何显示到页面上?最终是把Fragment的view添加到Fragment所在的ViewGroup中:

//FragmentManager.java


void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {

			...

         f.mContainer = container;
         f.performCreateView(f.performGetLayoutInflater(
                 f.mSavedFragmentState), container, f.mSavedFragmentState);
         if (f.mView != null) {
             f.mInnerView = f.mView;
             f.mView.setSaveFromParentEnabled(false);
             if (container != null) {
                 container.addView(f.mView);//把Fragment的view添加到Fragment所在的ViewGroup中
             }
             if (f.mHidden) {
                 f.mView.setVisibility(View.GONE);
             }
             f.onViewCreated(f.mView, f.mSavedFragmentState);


	...

}

Fragment的BackStack管理

//FragmentManager.java


    // Must be accessed while locked.
    ArrayList<BackStackRecord> mBackStackIndices; //mBackStackIndices 返回栈索引列表,用来给 BackStackRecord 分配 mIndex 需要
    ArrayList<Integer> mAvailBackStackIndices;//返回栈可用索引列表,存储着 mBackStackIndices 列表中值为 null 的下标
 
    // Temporary vars for removing redundant operations in BackStackRecords:
    ArrayList<BackStackRecord> mTmpRecords;//用于优化执行的临时变量,用来临时存储所有待执行的动作(mPendingActions)生成的 BackStackRecord,
    ArrayList<Boolean> mTmpIsPop;//用于优化执行的临时变量,用来存储 BackStackRecord 是否为出栈
    ArrayList<Fragment> mTmpAddedFragments;


    //用于给加入到返回栈的 BackStackRecord 分配 mIndex 下标,并加入到返回栈
    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;
            }
        }
    }

    //该方法是出栈的时候调用的,当 BackStackRecord 出栈后更新返回栈可用索引列表mAvailBackStackIndices的
    public void freeBackStackIndex(int index) {
        synchronized (this) {
            mBackStackIndices.set(index, null);//把相应位置设置成 null,然后把该下标加入可用返回栈列表的列尾
            if (mAvailBackStackIndices == null) {
                mAvailBackStackIndices = new ArrayList<Integer>();
            }
            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
            mAvailBackStackIndices.add(index);
        }
    }

FragmentManager用mAvailBackStackIndices和mBackStackIndices两个数组来为BackStackRecord分配Index以及存储。mAvailBackStackIndices用来存储在mBackStackIndices中能够分配的Index,mBackStackIndices则用来保存BackStackRecord。

这利用两个数组可以减少对mBackStackIndices的动态扩容以及数组复制的次数,是一个以空间换时间的策略。上面的代码首先判断是否有可用的Index分配给BackStackRecord,若无则直接将BackStackRecord插入到mBackStackIndices;若存在的话则从mAvailBackStackIndices的队尾取出一个index,然后在mBackStackIndices中的该index位置设置BackStackRecord值。

当从mBackStackIndices中pop出一个BackStackRecord时,并不会进行数组复制移动来回收这块内存,而是调用freeBackStackIndex()把BackStackRecord所在的mBackStackIndices的index这块内存置为null,然后把index加入mAvailBackStackIndices,这样当新的BackStackRecord入栈时,先从mAvailBackStackIndices中remove出一个index,然后把新的BackStackRecord插入到mBackStackIndices的index处。我们都知道ArrayList在插入和删除元素时会进行数组复制和移动,这样性能不高,这里采用额外加个mAvailBackStackIndices用来保存可用下标的方法,进行复用内存,避免了数组复制和移动,避免了内存的频繁分配与释放。

如下图(图只是画个大概,并不是精确画的):
在这里插入图片描述
下次新的BackStackRecord入栈时,会先从mAvailBackStackIndices中remove出一个index(这里即下标为2),然后把新的BackStackRecord插入到mBackStackIndices的index(即下标为2)处。

参考:
https://developer.android.google.cn/guide/components/fragments
https://developer.android.com/guide/topics/fundamentals/fragments.html

Fragment全解析系列(二):正确的使用姿势
Android Fragment 真正的完全解析(上)

Activity与Fragment的生命周期详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值