关于fragment backState的原理
在使用Fragment的时候我们一般会这样写:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.content_view, fragment, fragment.getClass().getName());
// transaction.addToBackStack(null);
transaction.commitAllowingStateLoss();
对于是否要加transaction.addToBackStack(null);也就是将Fragment加入到回退栈。官方的说法是取决于你是否要在回退的时候显示上一个Fragment。
虽然知道这回事,但是在做项目的时候还是没有清楚的认识,只是习惯性的加上addToBackStack;查看源码后才了解到该神马时候加入回退栈。
首先看
void addBackStackState(BackStackRecord state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
reportBackStackChanged();
}
可以看出,我们并不是将Fragment加入到回退栈,而是加了一个叫BackStackRecord的实例;那这个BackStackRecord到底是什么,简单的说一个BackStackRecord记录了一次操作。
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable
backstackRecord继承了FragmentTransaction抽象类,获得了诸如add,remove,replace这些控制方法,所以我们控制Fragment时一直使用的getSupportFragmentManager().beginTransaction()其实就是返回一个BackStackRecord实例;
backstackRecord也维护了一个Op对象,Op对象的作用就是记录一次操作的动作和Fragment引用以及操作使用的动画;
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
最后backstackRecord也实现了Runnable接口,通过commit来启动自身,在run方法中又根据维护的Op对象进行不同的操作。其实不同的Fragment操作就是在启动不同的BackStatcRecord线程。
下面我们已一次transaction.add操作为例:此操作也就是调用BackStackRecord里的add方法,方法中维护一个Op来保存这次add操作和相应的Fragment;然后我们会调用commit方法来提交操作,实质上是启动实现了Runnable接口的BackStackRecord自身,在run方法中根据Op执行add分支的操作,这里面我们会调用FragmentManager的addFragment方法
public void run() {
......
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
} break;
......
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
注意方法的最后会根据mAddToBackStack标识来判断是否加入到回退栈。
接下来在FragmentManager的addFragment方法中
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
if (DEBUG) Log.v(TAG, "add: " + fragment);
makeActive(fragment); //通过此方法将fragment加入到一个mActive列表里。
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
像上面注释里说的,通过makeActive方法将fragment加入到一个mActive列表。这个列表在后面会用到。但现在先来看看代码里用蓝色标记的两个方法,这是两个方法名相同的重载方法,他们最后都会调用一个非常重要的方法:moveToState
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
......
if (f.mState < newState) {
......
switch (f.mState) {
case Fragment.INITIALIZING:
......
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
......
}
case Fragment.ACTIVITY_CREATED:
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
f.performStart();
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
......
f.performResume();
......
}
}
} else if (f.mState > newState) {
switch (f.mState) {
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
......
f.performPause();
......
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
f.performStop();
}
case Fragment.STOPPED:
if (newState < Fragment.STOPPED) {
f.performReallyStop();
}
case Fragment.ACTIVITY_CREATED:
if (newState < Fragment.ACTIVITY_CREATED) {
......
}
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
......
f.performDestroy();
......
}
}
}
}
f.mState = newState;
}
对于这个方法要说明三点:
第一:方法里所有的分支只有
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.
这六种状态,好像不太够。其实这很好理解,如果传来的新状态比fragment的当前状态大那就是处于创建过程,如果新状态比当前状态小那就是处于关闭过程。闭上眼睛想一想就能转过弯儿了!!!
第二:这里面所有的case分支都是没有break方法的,这样就能保证传来一个状态就能把这个状态之后的所有操作都执行一遍,例如创建时传INITIALIZING状态,就能执行INITIALIZING、CREATED、ACTIVITY_CREATED、STOPPED、STARTED这一流程的代码,而不需要我们挨个的每个状态都传;又例如我们重写回到fragment要调用start()方法,那只需要传STOPPED(创建时执行的是onStart)就可以,而不需要再传STARTED(创建时执行的是onResume)。
第三:代码中的红色部分会调用FragmentActivity里的dispatchActivityXXX 方法,这里面最终会调用另外一个重要方法,它也叫做moveToState(其实这个方法最终也是会去调用上面的moveToState方法):
void moveToState(int newState, int transit, int transitStyle, boolean always) {
......
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();
}
}
}
这里面有个for循环,它会根据前面提到的mActive列表来调用存储fragment的moveToState方法(是上面的那个moveToState)。所以如果我们使用show、hide而不是用add、remove来操作fragment显示与隐藏的话,就会发现一个问题,假设一个FragmentActivity已经创建了三个fragment并且隐藏,然后它在创建第四个fragment的时候,会发现已经隐藏的三个fragment也都运行了onresume方法。这就是因为这三个fragment已经加入到mActive中,并且在创建第四个的时候循环调用了他们的resume方法。
现在回到最开始的问题,为什么说加入回退栈就可以实现按返回键退回到上一个fragment界面:
这就要看FragmentActivity里面的回退方法了
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
关键在判断条件,也就是popBackStackImmediate()方法的实现和他的返回值:
他的返回值是由popBackStackState(mActivity.mHandler, null, -1, 0)提供的(注意参数是固定的)
boolean popBackStackState(Handler handler, String name, int id, int flags) {
if (mBackStack == null) {
return false;
}
if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
int last = mBackStack.size()-1;
if (last < 0) {
return false;
}
final BackStackRecord bss = mBackStack.remove(last);
bss.popFromBackStack(true);
reportBackStackChanged();
} else {
......
}
return true;
}
注意方法的第一个判断条件:如果mBackStack == null 就直接return false,这样就会直接执行FragmentActivity的finishi()方法,这也就是当我们不添加addToBackStack方法时按返回键不会返回上一个fragment界面而是直接退出程序的原因了。
若添加了addToBackStack方法,也就是mBackStack != null 的情况下,根据固定的参数会进入蓝色代码段,在这里取出回退栈列表中的最后一条BackStackReco记录并执行它的popFromBackStack方法:
在这个方法里会根据BackStackRecord维护的Op对象来执行相应的操作,以replace操作为例:
case OP_REPLACE: {
Fragment f = op.fragment;
if (f != null) {
f.mNextAnim = op.popExitAnim;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
}
if (op.removed != null) {
for (int i=0; i<op.removed.size(); i++) {
Fragment old = op.removed.get(i);
old.mNextAnim = op.popEnterAnim;
mManager.addFragment(old, false);
}
}
} break;
从中可以清除的看出是把Op的当前fragment给remove掉,再把Op保存的old fragment给add上,这样一来就会显示上一个界面了。
所以,根据不同的情景,当我们不需要fragment会退到上一个界面或者管理的fragment过多而不想保留BackStackRecord记录过度使用资源时,就可以加入回退栈。
关于fragment backState的原理相关推荐
- fragment的工作原理_Preact(React)核心原理详解
原创: 宝丁 玄说前端 本文作者:字节跳动 - 宝丁 ---------------------------- 一.Preact 是什么 二.Preact 和 React 的区别有哪些? 三.Prea ...
- 关于fragment backstate的运用
这两天在使用Fragment做播放器里的播放列表和歌词显示两个界面的替换与更新时发现了很多问题,在此记录下问题及解决方法: 1.多个Fragment在replace后(并且都加入了后退栈ft.addT ...
- 关于fragment backstate的介绍
Transaction BackStack and its management Posted by Achin | Filed under Android 19.09.2014 This is se ...
- android4.4 fragment,在Activity和多个Fragment之间共享资源
在Activity和多个Fragment之间共享资源 Android Studio 默认生成的代码一般是这样的: package com.messy.lingplayer.playui import ...
- 字节跳动Android三面视频解析:framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等
前言 对于字节跳动的二面三面而言,Framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等问题都成高频问点!然而很多的朋友在面试时却答不上或者答不全!今天在这分享下这些 ...
- android 代码 lut,Android基于Shader的图像处理(7)-颜色表LUT
颜色表LUT(LookUp Table)主要用于滤镜,详细原理讲解参考这里<iOS 针对 LUT 滤镜的实现对比>.这里说下应用和注意的地方. fairy_tale.png 颜色表LUT在 ...
- 2015(2016届)校园招聘季——内推 篇
2015年8,9,10月份是互联网公司针对2016届学生校园招聘的高峰期,我有幸参加了几家不错的互联网公司的校园招聘,包括内推电话面试和校招现场面试,分享一些面试心得.具体记不得了,只记得些大概. 美 ...
- SpringBoot详细笔记记录
什么是微服务? 微服务就是要倡导大家尽量将功能进行拆分,将服务粒度做小,使之可以独立承担对外服务的职责,沿着这个思路开发和交付的软件服务实体就叫作"微服务",而围绕着这个思路和理念 ...
- 安卓开发论坛!阿里P8架构师的Android大厂面试题总结,附赠课程+题库
写这篇文章的目的是想说说这段时间一直被不断提起搞得人心惶惶的话题,裁员.为什么突然聊这个,本来一直是想避开这个话题的,一是网上已经有了铺天盖地的消息不想要再造成大家的恐慌,二是我身边几乎没有发生这样的 ...
最新文章
- aptana对齐快捷键ctrl+shift+f
- stm8s003程序跑飞_A股要大跌?跑不跑就看明天……
- 我从创建具有仅仅一年编码经验的视频游戏中学到了什么
- 阿里云使用js 实现OSS图片上传、获取OSS图片列表、获取图片外网访问地址(读写权限私有、读写权限公共);...
- 大数据的搜索引擎——Elasticsearch
- poj1860(spfa判正环)
- LeetCode 22. 括号生成(回溯+剪枝)
- paip.提升用户体验----gcc c++ JIT-debugging 技术
- 【元胞自动机】基于matlab元胞自动机车流密度不变下的双向两车道仿真(T 字形路口)【含Matlab源码 1290期】
- foxmail 批量导入收件人邮箱信息
- 十天学会单片机可能吗?单片机入门需要多久?
- 新媒体文章标题怎么写?
- android 渐变蒙版_干货分享(第八期):PS蒙版教学,利用蒙版制造渐隐效果
- 全国大学生英语竞赛——题型介绍
- 重生之我又是蝌蚪(召唤神龙)源码和无敌版
- EagleEye简介:户外视频监控分析和面部识别软件
- 自媒体数据运营saas_向媒体宣传您的SaaS
- CHIP-seq流程学习笔记(3)-比对软件 bowtie2
- 数组名和指针的区别和联系、数组名取地址a
- Kafka 入门教程(超详细)
热门文章
- 牛客网(剑指offer) 第三题 输入一个链表,从尾到头打印链表每个节点的值。
- [python] 安装numpy+scipy+matlotlib+scikit-learn及问题解决
- SwiftUI之如何使用@EnvironmentObject在视图之间共享数据
- 【机器视觉】 read_measure算子
- 【STM32】定时器相关函数和类型
- 【ARM】Tiny4412裸板编程之静态库(libc.a)
- 【Tools】MarkDown教程(三)-MarkDown表格和公式
- oracle里返回值吗,Oracle有返回值的存储过程 | 学步园
- struts2找不到action_第一次用上Struts2框架做Web开发的体验……
- Qt中 Qstring 与QbyteArray的互相转换