关于fragment backState的原理

Android技能树 — Fragment总体小结 1. 直接在Layout.xml中添加<Button/> <?xml version="1.0" encoding="utf-8"?> 2. 在代码中动态添加,比如我们添加到一个LinearLayout中: Button button = new Button(mActivity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_ 阅读详情

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

Android多回退栈实践(一)_安卓回退栈 在应用中,我们可以通过点击设备实体返回按键,或者应用左上角标题栏返回按钮进行返回。点击系统按钮返回点击应用左上角返回按钮从用户角度来讲,返回操作是一个类似栈的操作。点击返回时,之前的一系列界面,按照退栈的形式,依次退回。从开发角度来讲,这样一系列的回退操作,称之为回退栈。在还未出现Fragment的早期应用,我们一般是不需要关心回退栈的。用户使用如何进入,就如何退出。在使用Fragment之后,事情开始变得麻烦起来,开发者大多数采用了非常流程的单Activity、多Fragment。 阅读详情

相关推荐

ANSYS 2022R2 安装教程:系统准备、许可证配置与故障排查

ANSYS 是面向工程仿真的多物理场商业软件平台,其安装远非普通应用软件的图形化向导流程,而涉及操作系统版本、运行库精确版本、硬件虚拟化支持、许可证服务架构等系统级配置。理解 ANSYS License Manager 的服务机制与端口依赖、掌握 Visual C++ 和 .NET Framework 的启用逻辑,是保障 Fluent、Mechanical、HFSS 等求解器稳定调用的基础。该技术栈广泛应用于高校教学、航空航天结构分析、电子设备电磁兼容仿真等场景。本指南聚焦 ANSYS 2022R2 版本在

weixin_28559803的博客 598

简析 addToBackStack使用和Fragment执行流程

在使用Fragment的时候我们一般会这样写:         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();         transaction.replace(R.id.content_view, fragment, fragment.getClass().getName

米老唐鸭的专栏 9万+

AWS 小白书 [1] 全球架构

小白应该知道的 AWS 知识

boshansolo的博客 704

Android基础知识(十九):Fragment的使用及其addToBackStack()方法

Frament的使用及其addToBackStack()方法 一、Fragment的使用 此处仅仅介绍Fragment通过事务进行动态的添加,替换以及删除。 废话不多说,直接上代码。 如上述代码所示,Fragment的使用是通过一个Fragment的事务处理器进行的,通过调用add()、remove()、replace()方法对事务处理器中的Fragment进行添加、移除,替换,此外还可以调用s...

weixin_38196407的博客 5280

【农业生产系统模型】基于R语言APSIM模型进阶应用与参数优化、批量模拟实践技术

APSIM模型有Classic和Next Generation两个系列模型,能模拟几十种农作物、牧草和树木的土壤-植物-大气过程,被广泛应用于精细农业、水肥管理、气候变化、粮食安全、土壤碳周转、环境影响、农业可持续性、农业生态等诸多与农业生产和科研有关的领域。R语言是一门应用场景广泛、简单易学的程序语言,APSIM模型开发了许多R语言辅助包,在APSIM模型的气候、土壤、管理措施等数据准备,自动化模拟,参数优化和结果分析上都发挥着重要的作用。案例一:APSIM模型输入参数和土壤文件的制备。

2303_79243743的博客 1046

告别付费!手把手教你用DataV.GeoAtlas获取全国街道级GeoJSON数据(附完整避坑指南)

本文详细介绍了如何免费使用阿里云DataV.GeoAtlas工具获取全国街道级GeoJSON数据。通过可视化选取、格式转换、数据校验等步骤,提供了一套完整的避坑指南和工作流,帮助前端和可视化开发者高效解决高精度地图数据获取难题,节省成本并提升开发效率。

weixin_29085865的博客 900

Android-第八节02Fragment详解

一、Fragment的addToBackStack()使用 二、Fragment与Activity通信 三、从Activity向fragment发送

H_xxxxd的博客 2583

电镀技术:微型制造的原子级加法工艺与核心优势

电镀是一种利用电解原理在基材表面沉积金属层的表面处理技术,其本质是通过电场控制溶液中金属离子的迁移与还原,实现原子级的可控沉积。这一过程的核心在于对电流密度、温度、添加剂等参数的精密调控,从而实现对镀层结晶结构、性能的“编程”。从技术价值看,电镀作为一种“加法制造”工艺,在微观尺度上具有无与伦比的保形性和材料灵活性,能够实现复杂三维结构的均匀覆盖与功能复合。在应用场景上,它尤其契合现代微型化制造的需求,例如在集成电路的铜互连填充、高密度PCB板微孔金属化、MEMS传感器电极制备以及柔性电子器件制造中发挥着不

weixin_42524842的博客 528

43.在Fragment中实现返回栈

将详细介绍如何在Fragment中实现返回栈(Back Stack)。返回栈允许用户通过按下返回按钮导航到之前的Fragment。

2303_79132118的博客 509

Vue3 - Element Plus 表格组件 el-table “表格行数据中又嵌套了二维对象数组“,按平级循环渲染(嵌套数组视为同级行数据), 对嵌套的数组循环展示为表格,智能合并行或列详细教程

vue3,nuxt3,element-plus,table表格组件,el-table,vue3element表格组件,数据中有二维数组怎么遍历循环显示,表格数据带二级数组怎么循环渲染显示,表格数据中存在一个数组要怎么才能显示到表格,el-table表格合并详细教程示例,table表格如果对象里面还有对象数组那该怎么去渲染,平常都是数组中一个对象渲染成一行,每行中还有嵌套数组怎么办,每个行数据下还有一个数组怎么显示,怎么把表格相同的数据合并为一行,合并上下两行相同数据单元格,合并行或列如果遇到相同单元格就合

🏆 前端领域优质创作者 1万+

uniapp - 解决控制台提示“同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示”,uniApp安卓/苹果App真机运行调试显示同步资源失败,无任何授权提示且无法连接

uni-app,报错,安卓Android,苹果ios,安卓app端,苹果App端,uniapp苹果安卓app,提示错误解决,HbuilderX,uniapp同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示同步失败,安卓基座调试,没有反应,真机没有提示怎么办,如何解决这个问题,苹果ios手机,电脑模拟器运行,adb连接,连接失败,报错出错,uniapp真机运行的时候显示同步资源失败,未得到同步资源的授权,plugins里面没有tools,找不到tools,还是不行,重新打基座

🏆 前端领域优质创作者 1万+

Fragment的addToBackStack()使用

Fragment的addToBackStack()使用 官方文档介绍,这个方法的作用:当您移除或替换一个片段并向返回栈添加事务时,系统会停止(而非销毁)移除的片段。 如果用户执行回退操作进行片段恢复,该片段将重新启动。 如果您不向返回栈添加事务,则系统会在您移除或替换片段时将其销毁。 下面通过例子感受一下这个效果 先看一下主布局,就一个按钮和准备加载fragment的容器

贤鱼 1万+

YOLOV26详解 从原理到部署

计算机视觉领域最具影响力的目标检测框架迎来重大里程碑。2026年1月,Ultralytics 正式发布 YOLOv26——这不是一次简单的版本迭代,而是一场面向边缘计算时代的架构重构。本专栏基于 Ultralytics 官方技术白皮书、YOLO Vision 2025 大会首发内容及国际前沿研究成果,系统拆解 YOLOv26 从算法原理到工程落地的全栈技术体系。与常规教程不同,我们聚焦与,帮助你在端侧 AI 部署的实战中掌握核心竞争力。

u012133341的博客 2023

加入transaction.addToBackStack(null); 之后,仍直接退出问题解决。

FragmentTransaction.addToBackStack无效的问题:   如果当前的类继承的ActionBarActivity,则FragmentManager必须来自v4包,这样addToBackStack (null)是有效的,按返回键的时候可以返回上一个碎片。   import android.support.v4.app.FragmentManage

Solo_two的博客 1487

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

android------addToBackStack无作用问题

今天写了一个关于Fragment的Demo,有一部分代码是这样的:@Override public void onClick(View v) { switch (v.getId()){ case R.id.button: AnotherRightFragment anotherRightFragment=new Anot

俊光的专栏 2932

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Fragment addToBackStack时add和replace的区别

Fragment addToBackStack时add和replace的区别先看下activity的代码再看Fragment1. add2. replace总结:在调用addToBackStack时无论用add或者replace方式添加显示Fragment,在Activity里面添加几次Fragment就会有几个Fragment。只是他们的生命周期会不一样。add方式:生命周期不受其他Fragme...

aa234345的博客 1915

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

addToBackStack()

FragmentTransaction的addToBackStack()方法,可以接收一个名字描述返回栈的状态。

qq_34214349的博客 761

Android使用transaction.addToBackStack(null)加入返回栈无效

最近在看郭大神《第一行代码》上的碎片介绍,照着书上敲了transaction.addToBackStack(null)将事务加入返回栈,可是发现,点返回还是直接退出,不会返回到上一个碎片,Google半天中呼吁找到答案。先放上我原来的代码public class MainActivity extends AppCompatActivity implements View.OnClickListene

写代码的林克 7776

Fragment回退键addToBackStack无效的解决方法

今天在做Fragment的练习的时候就用到addToBackStack(null),在网上找了都是没办法解决的回答; 所有,我在这写写我是如何处理的。 这是我导入的包 import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction;

半路出道的songsong 3911

解决两个Fragment间调用addToBackStack()函数,按返回键直接退出的问题(即无法切换回上一个Fragment)

看《第一行代码》时,第三章的 “在碎片中模拟返回栈” 作者讲述如何从第二个Fragment切换回第一个Fragment时,我完完整整敲了一遍,发现并没有此效果,在网上找了大半天,各个博客也没有提,倒是发现不少网友们写的关于他这本书的读书笔记(基本是直接打上去的,这块也没做过多解释),好吧,不扯别的了,看看最后是怎么解决的。在 MainActivity 中重写 onBackPressed()函数

苏木云雨 1158

Android Fragment使用事项

1、Activity 必须继承AppCompatActivity package com.heima.googledemo; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.a...

yangjunjin的博客 1219

Fragment addToBackStack(null)的作用

Fragment常用于一个activity中展示多个相对独立的部分,利用add,show,hide进行fragment的添加,展示和隐藏。 FragmentA fragmentA = FragmentA .newInstance(parameter); getSupportFragmentManager().beginTransaction() ...

lidongxiu0714 5481

Fragment返回栈的手动管理

如何回到上一个Fragment 项目开中需要经常在不同的Fragment之间跳转,随时可能从一个Fragment页面跳到另一个Fragment页面。 这些页面的切换还是比较简单的,根据业务需求,相应地调用FragmentTransaction的show()、add()、hide()、replace()等方法,再提交事务就可以了。 当页面不是很多时,这种方法足以应付了。 但“返回上一页”这种需求随着...

binaryshao 1847
上一篇: 关于fragment backstate的运用
下一篇: 关于Fragment、Tabhost和FragmentPagerAdapter来实现导航栏的效果
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值