概述
Fragment从android3.0版本开始引入。随着界面布局的复杂化,处理起来也更加复杂,引入Fragment可以把Activity拆成各个部分,每个Fragment都有自己的布局和生命周期,方便了开发。采用Fragment而不是Activity进行应用的UI管理,可绕开android系统规则的限制。
Fragment是一个控制器对象,Activity可委派它完成一些任务,通常这些任务就是管理用户界面。受管的用户界面可以是一整屏或是整屏的一部分。管理用户界面的Fragment又称为UI Fragment。它也有自己产生于布局文件的视图。Fragment视图包含了用户可以交互的可视化UI元素。
Activity托管Fragment,暂时可以把托管理解成Activity在其视图层级里提供一处位置来放置Fragment的视图。Fragment本身不具有在屏幕上显示视图的能力,因此,只有将它的视图放置在Activity的视图层级结构中,Fragment视图才能显示在屏幕上。
使用Fragment的好处就是可以把业务逻辑与UI封装在一起,与外部关联不大,其他程序也可以用该组件,从而实现复用最大化。
Fragment的生命周期
Fragment必须是依存于Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。下图是Fragment与Activity的生命周期关系图:
可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach(Activity)
当Fragment与Activity发生关联时调用
onCreateView(LayoutInflater,ViewGroup,Bundle)
创建Fragment的视图
onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用
onDestroyView()
与onCreateView相对应,当该Fragment的视图被移除时调用
onDetach()
与onAttach向对应,当Fragment与Activity关联被取消时调用
Fragment家族常用的API
Fragment常用的3个类:
- Fragment:主要用于定义Fragment
- FragmentManager:主要用于在Activity中操作Fragment
- FragmentTransaction:保证一系列Fragment操作的原子性,表示Fragment的事务操作
管理Fragment回退栈
类似于android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果将Fragment任务添加到回退栈,当用户点击后退按钮后,将看到上一次保存的Fragment,一旦Fragment完全从后退栈中弹出,用户再次点击后退栈,则退出当前Activity。
添加一个Fragment事务到回退栈:
|
|
Fragment与Activity通信
因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:
- 如果Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
- 如果Activity中未保存任何Fragment的引用,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
- 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作
Fragment的显示原理
一般情况下,要显示一个fragment的时候,会使用如下代码:
|
|
这段代码十分常用,接下来进一步看看fragment界面是怎样显示在收集屏幕上的。
首先从Activity的getFragmentManager()方法开始。
|
|
|
|
|
|
通过上面代码,可以看出Activity的getFragmentManager()方法主要借助于其FragmentController来实现,而FragmentController内部持有的FragmentHostCallback抽象类才是主要逻辑的承担者,其具体实现类是Activity的内部类HostCallbacks,HostCallbacks继承于FragmentHostCallback,并且在初始化的时候把Activity的引用也传了进去,所以这个对象持有本身activity的引用,同时也持有activity的context和handler引用。最后Activity的getFragmentManager()方法返回的是FragmentManagerImpl这个类的对象。
接下来看看FragmentManagerImpl的beginTransaction()方法。
|
|
可以看到FragmentManagerImpl的beginTransaction()方法返回的是BackStackRecord类的对象,BackStackRecord类继承了FragmentTransaction类,同时实现了FragmentManager.BackStackEntry和FragmentManagerImpl.OpGenerator两个接口。
接下来继续看看这个BackStackRecord对象的操作。
|
|
可以看到add操作把fragment保存在了Op对象中,并存放进mOps这个ArrayList中。而FragmentTransaction的commit()方法则是调用了FragmentManagerImpl对象的enqueueAction()方法。
接着来看FragmentManagerImpl的enqueueAction()方法实现。
|
|
从上面可以看出enqueueAction方法主要调用了scheduleCommit方法,而scheduleCommit方法通过mHost的handler将mExecCommit这个Runnable发送到activity的主线程去执行,在mExecCommit的run方法中调用了execPendingActions方法,之后通过一些列调用执行了BackStackRecord对象的executePopOps方法和FragmentManagerImpl的moveToState方法。
先来看BackStackRecord对象的executePopOps方法。
|
|
根据代码,可以知道add操作最终通过mManager的addFragment方法实现。
|
|
可以看出moveToState方法最终通过container.addView(f.mView)把fragment的界面设置在了容器里,之后就是ViewGroup的addView了,这里就会追溯到WMS的窗口添加原理和View的绘制原理了,不在此做深入,至此,fragment显示到屏幕的整个过程就分析完毕了。
Fragment的缓存和恢复机制
保存fragment状态的主要是靠FragmentState和FragmentManagerState 这个两个类来完成的。
|
|
|
|
接下来以FragmentActivity为例看一下其生命周期给Fragment带来的影响。
|
|
|
|
|
|
可以看出当FragmentActivity执行onStop时无非就是把fragment的状态转换为Fragment.STOPPED,fragment的实例并没有销毁还存在着。
Fragment的恢复机制主要是通过setRetainInstance来设置的,通过在onCreate方法中设置其为true来达到保存数据的目的。这样,在屏幕方向改变时,fragment的实例不会被销毁,它只是被销毁了视图,并且从Activity上解绑,然后重新创建的时候就只会创建视图,因为实例还存在,所以不走onCreate方法。使用这种方法的时候,fragment不能加入到回退栈中,而且只适用于因为配置改变比如屏幕方向改变导致的fragment实例可能被销毁的状况,而如果因为应用处于后台或者内存紧张等原因,fragment的实例还是可能被销毁的。