Fragment源码分析
转载请标明出处:http://blog.csdn.net/shensky711/article/details/53171248
本文出自: 【HansChen的博客】
概述
Fragment
表示 Activity 中的行为或用户界面部分。您可以将多个 Fragment 组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个 Fragment。您可以将 Fragment 视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除 Fragment。
Fragment 必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的所有 Fragment 也会暂停;当 Activity 被销毁时,所有 Fragment 也会被销毁。 不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个 Fragment,如添加或移除它们。 当您执行此类 Fragment 事务时,您也可以将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生 Fragment 事务的记录。 返回栈让用户可以通过按返回按钮撤消 Fragment 事务(后退)。
当您将 Fragment 作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且 Fragment 会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明Fragment,将其作为 <fragment>
元素插入您的 Activity 布局中,或者通过将其添加到某个现有 ViewGroup,利用应用代码进行插入。不过,Fragment 并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的 Fragment 用作 Activity 的不可见工作线程。
本文将通过分析源码,对 Fragment 的创建、销毁以及生命周期做一个更深入的认识。
建议读者在看这篇文章的时候,先看下Fragment事务管理源码分析,对Fragment管理类先有一个比较清楚的认识。
分析入口
/**
* 构造并显示Fragment
*
* @param containerViewId 容器控件id
* @param clz Fragment类
*/
protected void showFragment(@IdRes int containerViewId, Class<? extends Fragment> clz) {
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();//开始事务管理
try {
Fragment f = clz.newInstance();
ft.add(containerViewId, f, clz.getName());//添加操作
ft.commit();//提交事务
} catch (Exception e) {
e.printStackTrace();
}
}
上面的代码就是动态地往containerViewId
里添加一个Fragment并让它显示出来,可以看到,这个涉及到Fragment的事务管理,详细可以参考Fragment事务管理源码分析,这里就不再阐述了。
代码分析
BackStackRecord#run
调用了commit之后,真正执行的地方是在BackStackRecord的run方法:
public void run() {
......
if (mManager.mCurState >= Fragment.CREATED) {
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
calculateFragments(firstOutFragments, lastInFragments);
beginTransition(firstOutFragments, lastInFragments, false);
}
//遍历链表,根据cmd事务类型依次处理事务
Op op = mHead;
while (op != null) {
switch (op.cmd) {
case OP_ADD: {
//添加一个新的Fragment
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
break;
case OP_REPLACE: {
Fragment f = op.fragment;
int containerId = f.mContainerId;
if (mManager.mAdded != null) {
for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
Fragment old = mManager.mAdded.get(i);
if (old.mContainerId == containerId) {
if (old == f) {
op.fragment = f = null;
} else {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
}
op.removed.add(old);
old.mNextAnim = op.exitAnim;
if (mAddToBackStack) {
old.mBackStackNesting += 1;
}
mManager.removeFragment(old, mTransition, mTransitionStyle);
}
}
}
}
if (f != null) {
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
}
break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.removeFragment(f, mTransition, mTransitionStyle);
}
break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.hideFragment(f, mTransition, mTransitionStyle);
}
break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.showFragment(f, mTransition, mTransitionStyle);
}
break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.detachFragment(f, mTransition, mTransitionStyle);
}
break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.attachFragment(f, mTransition, mTransitionStyle);
}
break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
op = op.next;
}
mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
因为我们调用的是add操作,所以执行的代码片段是:
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
break;
参数解释:
- op.fragment:showFragment中创建的Fragment实例,并且现在Fragment的mTag、mFragmentId、mContainerId已被初始化过了
- op.enterAnim:入场动画,可以先不管
- mManager:FragmentManagerImpl实例
FragmentManagerImpl#addFragment
public void addFragment(Fragment fragment, boolean moveToStateNow) {
//已添加的Fragment列表
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
//设置Fragment的mIndex,并把Fragment添加到mActive列表
makeActive(fragment);
//判断是否被detach。默认为false
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
//把Fragment添加到mAdded列表
mAdded.add(fragment);
//设置Fragment标记位
fragment.mAdded = true;
fragment.mRemoving = false;
//判断是否需要刷新菜单
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
//在这次分析中moveToStateNow为false,moveToState方法在本方法外层方法中调用
if (moveToStateNow) {
moveToState(fragment);
}
}
}
addFragment里面把Fragment加入mActive和mAdded列表,并且设置标记为fragment.mAdded
为true,fragment.mRemoving
为false。
执行完ADD操作后,执行moveToState,moveToState顾名思义,就是把Fragment变为某种状态
//mManager.mCurState的状态很重要,我们下面会分析它现在处于什么状态
mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true);
//添加本次操作到回退栈中
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
Fragment状态
我们知道Fragment的生命周期是依赖于Activity的,比如Activity处于onResume,那么Fragment也会处于onResume状态,这里的参数mManager.mCurState对应的状态有:
static final int INVALID_STATE = -1; // Invalid state used as a null value.
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.
mCurState的初始状态是Fragment.INITIALIZING,那么在BackStackRecord中调用moveToState的时候,mCurState是什么值呢?它是会受Activity生命周期影响而变化的,我们来看下FragmentActivity
的代码
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
//绑定FragmentManager
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
... ...
//分发Fragment的create事件
mFragments.dispatchCreate();
}
public void dispatchCreate() {
mHost.mFragmentManager.dispatchCreate();
}
public void dispatchCreate() {
mStateSaved = false;
//注意这里设置了新的state
moveToState(Fragment.CREATED, false);
}
void moveToState(int newState, boolean always) {
moveToState(newState, 0, 0, always);
}
void moveToState(int newState, int transit, int transitStyle, boolean always) {
... ...
//给mCurState赋值
mCurState = newState;
... ...
}
在onCreate中把mCurState变为Fragment.CREATED
状态了,Activity的其他生命周期方法回调的时候,也会改变这个状态,大致整理如下:
- onCreate:Fragment.CREATED
- onStart:Fragment.ACTIVITY_CREATED–>Fragment.STARTED (Fragment.ACTIVITY_CREATED只会在Activity创建之后触发一次,Fragment.STARTED每次onStart的时候都会触发)
- onResume:Fragment.RESUMED
- onPause:Fragment.STARTED
- onStop:Fragment.STOPPED
- onDestroy:Fragment.INITIALIZING
下面是一张状态迁移图:
所以随着Activity生命周期的推进,Activity内所有Fragment的生命周期也会跟着推进。从Activity创建到显示出来,最后会处于onResume状态,那么我们这次就直接分析当前Activity处于onResume调用之后的情形好了。所以假定现在mCurState为Fragment.RESUMED,
让我们继续跟踪FragmentManagerImpl
FragmentManagerImpl#moveToState
void moveToState(int newState, int transit, int transitStyle, boolean always) {
if (mHost == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No activity");
}
if (!always && mCurState == newState) {
return;
}
mCurState = newState;
if (mActive != null) {
boolean loadersRunning = false;
//遍历所有Active状态的Fragment,改变所有Fragment的状态
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null) {
//关键代码
moveToState(f, newState, transit, transitStyle, false);
if (f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
}
if (!loadersRunning) {
startPendingDeferredFragments();
}
//让Activity刷新Menu
if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
mHost.onInvalidateOptionsMenu();
mNeedMenuInvalidate = false;
}
}
}
设置最新的mCurState状态,通过上面的分析,我们知道newState等于Fragment.RESUMED。遍历mActive列表中保存的Fragment,改变Fragment状态,这里又调用了一个moveToState
方法,这个方法就是真正回调Fragment生命周期的地方
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
// Fragments被detach或Fragment没有添加到mAdded列表的话,设置目标Fragment的新状态为CREATED状态,此次分析中不会进入这个分支
if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
newState = Fragment.CREATED;
}
//此次分析中f.mRemoving为false
if (f.mRemoving && newState > f.mState) {
// While removing a fragment, we can't change it to a higher state.
newState = f.mState;
}
// 是否延时启动
if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
newState = Fragment.STOPPED;
}
if (f.mState < newState) {
//此次命中的分支
......
//根据Fragment当前的状态,选择case的分支。需要注意的是,这里的switch case是没有break语句的。这种设计可以让Fragment把自身的状态依次推进到目标状态
switch (f.mState) {
case Fragment.INITIALIZING:
if (f.mSavedFragmentState != null) {
......
}
f.mHost = mHost;
//mParent是在FragmentActivity的onCreate方法中调用attachHost传进来的,传进来的是空值
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
f.mCalled = false;
//【Fragment生命周期】onAttach回调,里面会把mCalled设置为true
f.onAttach(mHost.getContext());
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
//让Activity可以监听到Fragment的attach
mHost.onAttachFragment(f);
} else {
f.mParentFragment.onAttachFragment(f);
}
//f.mRetaining默认为false
if (!f.mRetaining) {
//关键代码,内部会调用【Fragment生命周期】onCreate
f.performCreate(f.mSavedFragmentState);
} else {
f.restoreChildFragmentState(f.mSavedFragmentState, true);
f.mState = Fragment.CREATED;
}
f.mRetaining = false;
//Fragment是否定义在Layout文件的<fragment>标签中的,本次栗子为代码动态添加Fragment,所以为false
if (f.mFromLayout) {
// For fragments that are part of the content view
// layout, we need to instantiate the view immediately
// and the inflater will take care of adding it.
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), null, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
}
}
//注意,这里没有break
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
if (!f.mFromLayout) {
//开始创建Fragment的view
ViewGroup container = null;
if (f.mContainerId != 0) {
if (f.mContainerId == View.NO_ID) {
throwException(new IllegalArgumentException(""));
}
//调用Activity的findViewById方法查找控件
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
......
}
}
f.mContainer = container;
//关键代码,内部会调用【Fragment生命周期】onCreateView,并返回Fragment中new出的视图
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
//设置入场动画
Animator anim = loadAnimator(f, transit, true,
transitionStyle);
if (anim != null) {
anim.setTarget(f.mView);
setHWLayerAnimListenerIfAlpha(f.mView, anim);
anim.start();
}
//把Fragment的view加入到父控件
container.addView(f.mView);
}
if (f.mHidden) f.mView.setVisibility(View.GONE);
//【Fragment生命周期】onViewCreated回调
f.onViewCreated(f.mView, f.mSavedFragmentState);
}
}
//关键代码,内部会调用【Fragment生命周期】onActivityCreated
f.performActivityCreated(f.mSavedFragmentState);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
case Fragment.ACTIVITY_CREATED:
if (newState > Fragment.ACTIVITY_CREATED) {
f.mState = Fragment.STOPPED;
}
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
//关键代码,内部会调用【Fragment生命周期】onStart
f.performStart();
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
//关键代码,内部会调用【Fragment生命周期】onResume
f.performResume();
// Get rid of this in case we saved it and never needed it.
f.mSavedFragmentState = null;
f.mSavedViewState = null;
}
}
} else if (f.mState > newState) {
//state降级处理
......
}
if (f.mState != newState) {
f.mState = newState;
}
}
这段代码逻辑还是比较长,我把注释写在代码里了。可以看到,这个代码写得很巧妙,通过switch case控制,可以一层一层地把Fragment的生命周期推进下去,比如当前fragnemt的state是Fragment.STARTED
,那么它就只会执行performResume,如果Fragment的状态是Fragment.INITIALIZING
,那么就会从switch的最开始依次执行下来,把Fragment的生命周期onAttach–>onResume依次调用。
简要说明下上面的代码:
- mHost是FragmentHostCallback抽象类的实例,它的实现类是Activity的HostCallbacks
- mParent为null
- mHost.getContext()获取的context就是宿主Activity实例
- Fragment中创建的View会自动通过container.addView(f.mView)添加到父控件中
很多Fragment的生命周期是通过Fragment的performXxx()方法去调用的,比如:
void performCreate(Bundle savedInstanceState) {
......
onCreate(savedInstanceState);
......
}
View performCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
......
return onCreateView(inflater, container, savedInstanceState);
}
void performActivityCreated(Bundle savedInstanceState) {
......
onActivityCreated(savedInstanceState);
......
}
void performStart() {
......
onStart();
......
}
void performResume() {
......
onResume();
......
}
Fragment状态的降级操作
有些童鞋们可能会有疑问,上面只分析到了onAttach->onResume生命周期的回调,那onPause、onDestroy等方法又是什么时候执行的呢?我们再看下刚才的代码
if (f.mState < newState) {
......
} else if (f.mState > newState) {
//state降级处理
......
}
答案就是在else if分支里面,比如当Acivity锁屏的时候,就Activity生命周期会自动回调onPause,从而触发dispatchPause,在里面调用moveToState(Fragment.STARTED, false);
由于Fragment当前的状态是RESUMED状态,大于newState,所以就会走else if的分支,触发相应的生命周期方法。else if分支的逻辑和state升级的差不多,这里就再进行分析了
生命周期
最后,放张官网上公布的Fragment生命周期图,通过代码分析,我们发现代码的中生命周期的调用顺序和图中确实是一致的
总结
本文大致地从源码的角度分析了Fragment创建、生命周期回调的过程,如果读者对Fragment的remove
、replace
、hide
、detach
、attach
等操作有兴趣的话,可以自行分析,核心代码主要在BackStackRecord类的run方法以及FragmentManagerImpl的moveToState方法中。
Fragment源码分析的更多相关文章
- Spring源码分析——资源访问利器Resource之实现类分析
今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...
- angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...
- jQuery.clean()方法源码分析(一)
在jQuery 1.7.1中调用jQuery.clean()方法的地方有三处,第一次就是在我之前的随笔分析jQuery.buildFramgment()方法里面的,其实还是构造函数的一部分,在处理诸如 ...
- jQuery.buildFragment源码分析以及在构造jQuery对象的作用
这个方法在jQuery源码中比较靠后的位置出现,主要用于两处.1是构造jQuery对象的时候使用 2.是为DOM操作提供底层支持,这也就是为什么先学习它的原因.之前的随笔已经分析过jQuery的构造函 ...
- Android Small插件化框架源码分析
Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...
- Backbone.js源码分析(珍藏版)
源码分析珍藏,方便下次阅读! // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone ...
- Android网络框架源码分析一---Volley
转载自 http://www.jianshu.com/p/9e17727f31a1?utm_campaign=maleskine&utm_content=note&utm_medium ...
- Volley源码分析(2)----ImageLoader
一:imageLoader 先来看看如何使用imageloader: public void showImg(View view){ ImageView imageView = (ImageView) ...
- Fresco 源码分析(一) DraweeView-DraweeHierarchy-DraweeController(MVC) DraweeView的分析
4. Fresco的内容 为了方便学习,我们先从使用结合官方的文档来分析 4.1 Fresco客户端的使用 在使用Fresco的使用,我们直接使用的是SimpleDraweeView这个类,然后在Ac ...
随机推荐
- django-模型之从数据库获取数据(二)
1.获取一条数据(字段值必须唯一) 2.条件查询filter 3.排除查询exclude 4.链式查询 5.查询后进行排序order_by 6.按字段查询values 7.插入数据create 8.数 ...
- django-URL实例命名空间(十一)
每生成一个地址,都是一个实例.使用实例命名空间,针对于一个app而言. book/views.py from django.http import HttpResponse from django.s ...
- java中的Static、final、Static final各种用法详解
前言 对Static.final.Static final这几个关键词熟悉又陌生?想说却又不知怎么准确说出口?好的,本篇博客文章将简短概要出他们之间的各自的使用,希望各位要是被你的面试官问到了,也能从 ...
- GCC常用参数详解
转载:http://www.cnblogs.com/zhangsir6/articles/2956798.html 简介gcc and g++现在是gnu中最主要和最流行的c & c++编译器 ...
- SpringBoot 2.0 开发案例之百倍级减肥瘦身之旅
前言 为了存我的小黄图,最近在做一款图床服务,集成了各种第三方云存储服务,目前正在内部测试阶段.项目是以Jar的形式运行在腾讯云上,不要问我为什么使用腾讯云了,因为阿里云老用户和狗不得入内. 问题凸显 ...
- SSM简易版
技术准备 Java: 基础知识 框架: Spring,SpringMVC,Mybatis 数据库: Mysq 开发工具: Eclipse,Maven 项目结构 数据库设计 创建数据库:student ...
- 提高首屏页面加载速度,解决vue-cli打包后单个文件过大的问题
本教程是针对vue-cli3以上的版本,其实原理都大同小异,这个demo为vue-cli直接创建的项目,并在main.js中引入了echart.element-ui.lodash 首先看demo打包后 ...
- CSPS模拟测试59
这场考得我心态爆炸......... 开场T1只会$n^{2}$,然后发现bfs时每个点只需要被更新一次,其他的更新都是没用的. 也就是说,我们可以只更新还没被更新的点? 于是我先YY了一个链表,发现 ...
- CSPS模拟 50
收获很多,良心出题人 T1 施工 研究半天,最后30分暴力走人 考后看了题解,稍神仙这题弃对了...... 要拿30+,必须发现要填的话一定是填一个坑使它底部变平,最终底部高度小于等于两边 为什么是坑 ...
- 『题解』Codeforces121A Lucky Sum
更好的阅读体验 Portal Portal1: Codeforces Portal2: Luogu Description Petya loves lucky numbers. Everybody k ...