`
qilixiang012
  • 浏览: 203192 次
文章分类
社区版块
存档分类
最新评论

android 简析 addToBackStack使用和Fragment执行流程

 
阅读更多

在使用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 implementsFragmentManager.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记录过度使用资源时,就可以加入回退栈。

分享到:
评论

相关推荐

    2023年美赛特等奖论文-F-2305794-解密.pdf

    大学生,数学建模,美国大学生数学建模竞赛,MCM/ICM,2023年美赛特等奖O奖论文

    亚太经社会:2024年亚太贸易便利化报告.pdf

    亚太经社会:2024年亚太贸易便利化报告.pdf

    消费者价格指数下的年通货膨胀率(1960-2021年).xls

    消费者价格指数(CPI):按消费者价格指数衡量的通货膨胀反映出普通消费者在指定时间间隔(如年度)内购买固定或变动的一篮子货物和服务的成本的年百分比变化。通常采用拉斯佩尔公式进行计算。

    node-v8.10.0-linux-x86.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    网页制作基础学习--HTML+CSS常用代码.txt

    网页制作基础学习--HTML+CSS常用代码

    IEC TS 60364-8-3-2020 低压电气装置.第8-3部分:功能方面.生产用户电气装置的操作.pdf

    IEC TS 60364-8-3-2020 低压电气装置.第8-3部分:功能方面.生产用户电气装置的操作.pdf

    智能制造数字化供应链全栈解决方案.pptx

    智能制造数字化供应链全栈解决方案.pptx

    yolov5训练自己的数据集.docx

    yolov5训练自己的数据集

    Windows 10系统上安装和配置Tomcat的步骤

    附件是Windows 10系统上安装和配置Tomcat的步骤,文件绿色安全,请大家放心下载,仅供交流学习使用,无任何商业目的!

    2018美赛O奖论文C题合集.pdf

    大学生,数学建模,美国大学生数学建模竞赛,MCM/ICM,历年美赛特等奖O奖论文

    超级实用简洁的反色软件,支持部分区域反色!!

    超级实用简洁的反色软件,支持部分区域反色!! 界面简洁,易于使用! 源码可以联系

    2024年中国NAD+抗衰补剂行业研究报告.docx

    2024年中国NAD+抗衰补剂行业研究报告

    高德地图API+Python解决租房问题内含源码和设计文档.md

    高德地图API+Python解决租房问题内含源码和设计文档.md

    QYResearch:2023年前10大壁纸刀企业占据全球39%的市场份额.docx

    QYResearch:2023年前10大壁纸刀企业占据全球39%的市场份额.docx

    广东工业大学-数据库简答题考试试题回忆版以及答案解析.doc

    此试题是考试后回忆版本,你会发现是惊喜。恭喜你考个好成绩。

    node-v9.11.0-linux-s390x.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    MVIMG_20240506_214323.jpg

    MVIMG_20240506_214323.jpg

    2023年美赛特等奖论文-B-2318300-解密.pdf

    大学生,数学建模,美国大学生数学建模竞赛,MCM/ICM,2023年美赛特等奖O奖论文

    数据库课程设计的概要介绍与分析

    数据库是计算机科学中的一个核心领域,它用于存储、管理和检索数据。随着信息技术的发展,数据库技术已经渗透到我们日常生活的方方面面,从简单的网站用户信息管理到复杂的企业级数据仓库系统,无一不彰显其重要性。以下是一个关于数据库的综合资源描述,旨在为初学者和专业人士提供一个全面的视角。 ### 1. 数据库基础 数据库的基础知识包括了解数据模型、数据库管理系统(DBMS)以及SQL语言等。数据模型主要有关系型模型(如MySQL、Oracle)、非关系型模型(NoSQL,如MongoDB、Cassandra)等。关系型数据库遵循ACID特性(原子性、一致性、隔离性、持久性),适用于需要高度一致性的场景;而非关系型数据库则以灵活的schema设计和高可扩展性见长,适用于大数据处理和实时Web应用。 ### 2. 学习资源 - **在线课程**:Coursera、edX、Udacity等平台提供了多门数据库相关的课程,涵盖从入门到高级的各种主题,比如Stanford大学的《数据库系统概念》课程。 - **书籍**:《数据库系统概论》(Silberschatz, Korth, Sudarsha

    《统计与数据分析基础》03数据处理.pptx

    《统计与数据分析基础》03数据处理

Global site tag (gtag.js) - Google Analytics