(一百三十七)梳理fragment的isAdded

2023-12-19 07:38

本文主要是介绍(一百三十七)梳理fragment的isAdded,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.api

2.流程梳理

2.1 addFragment

2.2 attachFragment

2.3 restoreAllState

3.总结


1.api

/frameworks/base/core/java/android/app/Fragment.java

    /*** Return true if the fragment is currently added to its activity.*/final public boolean isAdded() {return mHost != null && mAdded;}

isAdded表示当前fragment有没有被加入到activity,那add具体是指的什么呢?在哪里被设为true?mAdded在fragment中只有初始化

    // True if the fragment is in the list of added fragments.boolean mAdded;

由于它是个Default权限的,所有只能是同包或者子类有权修改。

 

2.流程梳理

那么fragment的mAdded的参数只有是FragmentManager会改,看下具体改动的地方

搜了下有3个方法

  • addFragment
  • attachFragment
  • restoreAllState

2.1 addFragment

    public void addFragment(Fragment fragment, boolean moveToStateNow) {if (DEBUG) Log.v(TAG, "add: " + fragment);makeActive(fragment);if (!fragment.mDetached) {if (mAdded.contains(fragment)) {throw new IllegalStateException("Fragment already added: " + fragment);}synchronized (mAdded) {mAdded.add(fragment);}fragment.mAdded = true;fragment.mRemoving = false;if (fragment.mView == null) {fragment.mHiddenChanged = false;}if (fragment.mHasMenu && fragment.mMenuVisible) {mNeedMenuInvalidate = true;}if (moveToStateNow) {moveToState(fragment);}}}

这里可以看到有个mAdded的ArrayList,会将add的fragment加进去,加进去后将fragment的标志位置为true。

    final ArrayList<Fragment> mAdded = new ArrayList<>();

接着看下addFragment哪里有调用

    @Overridepublic View onCreateView(View parent, String name, Context context, AttributeSet attrs) {if (!"fragment".equals(name)) {return null;}String fname = attrs.getAttributeValue(null, "class");TypedArray a =context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);if (fname == null) {fname = a.getString(com.android.internal.R.styleable.Fragment_name);}int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);a.recycle();int containerId = parent != null ? parent.getId() : 0;if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {throw new IllegalArgumentException(attrs.getPositionDescription()+ ": Must specify unique android:id, android:tag, or have a parent with"+ " an id for " + fname);}// If we restored from a previous state, we may already have// instantiated this fragment from the state and should use// that instance instead of making a new one.Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;if (fragment == null && tag != null) {fragment = findFragmentByTag(tag);}if (fragment == null && containerId != View.NO_ID) {fragment = findFragmentById(containerId);}if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"+ Integer.toHexString(id) + " fname=" + fname+ " existing=" + fragment);if (fragment == null) {fragment = mContainer.instantiate(context, fname, null);fragment.mFromLayout = true;fragment.mFragmentId = id != 0 ? id : containerId;fragment.mContainerId = containerId;fragment.mTag = tag;fragment.mInLayout = true;fragment.mFragmentManager = this;fragment.mHost = mHost;fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);addFragment(fragment, true);} else if (fragment.mInLayout) {// A fragment already exists and it is not one we restored from// previous state.throw new IllegalArgumentException(attrs.getPositionDescription()+ ": Duplicate id 0x" + Integer.toHexString(id)+ ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)+ " with another fragment for " + fname);} else {// This fragment was retained from a previous instance; get it// going now.fragment.mInLayout = true;fragment.mHost = mHost;// If this fragment is newly instantiated (either right now, or// from last saved state), then give it the attributes to// initialize itself.if (!fragment.mRetaining) {fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);}}// If we haven't finished entering the CREATED state ourselves yet,// push the inflated child fragment along. This will ensureInflatedFragmentView// at the right phase of the lifecycle so that we will have mView populated// for compliant fragments below.if (mCurState < Fragment.CREATED && fragment.mFromLayout) {moveToState(fragment, Fragment.CREATED, 0, 0, false);} else {moveToState(fragment);}if (fragment.mView == null) {throw new IllegalStateException("Fragment " + fname+ " did not create a view.");}if (id != 0) {fragment.mView.setId(id);}if (fragment.mView.getTag() == null) {fragment.mView.setTag(tag);}return fragment.mView;}

看起来是fragment创建的生命周期有调用。

 

2.2 attachFragment

    public void attachFragment(Fragment fragment) {if (DEBUG) Log.v(TAG, "attach: " + fragment);if (fragment.mDetached) {fragment.mDetached = false;if (!fragment.mAdded) {if (mAdded.contains(fragment)) {throw new IllegalStateException("Fragment already added: " + fragment);}if (DEBUG) Log.v(TAG, "add from attach: " + fragment);synchronized (mAdded) {mAdded.add(fragment);}fragment.mAdded = true;if (fragment.mHasMenu && fragment.mMenuVisible) {mNeedMenuInvalidate = true;}}}}

这里也是将fragment加进mAdded中去并将fragment的mAdded设为true,但是不同的是可以fragment.mDetached为true的时候才会attach并且没有added.不同于之前addFragment是fragment.mDetached为false才走进来。

看了下attachFragment是只有如下会调用

/frameworks/base/core/java/android/app/BackStackRecord.java

    /*** Executes the operations contained within this transaction. The Fragment states will only* be modified if optimizations are not allowed.*/void executeOps() {final int numOps = mOps.size();for (int opNum = 0; opNum < numOps; opNum++) {final Op op = mOps.get(opNum);final Fragment f = op.fragment;if (f != null) {f.setNextTransition(mTransition, mTransitionStyle);}switch (op.cmd) {case OP_ADD:f.setNextAnim(op.enterAnim);mManager.addFragment(f, false);break;case OP_REMOVE:f.setNextAnim(op.exitAnim);mManager.removeFragment(f);break;case OP_HIDE:f.setNextAnim(op.exitAnim);mManager.hideFragment(f);break;case OP_SHOW:f.setNextAnim(op.enterAnim);mManager.showFragment(f);break;case OP_DETACH:f.setNextAnim(op.exitAnim);mManager.detachFragment(f);break;case OP_ATTACH:f.setNextAnim(op.enterAnim);mManager.attachFragment(f);break;

还有个逆过程

    /*** Reverses the execution of the operations within this transaction. The Fragment states will* only be modified if reordering is not allowed.** @param moveToState {@code true} if added fragments should be moved to their final state*                    in ordered transactions*/void executePopOps(boolean moveToState) {for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {final Op op = mOps.get(opNum);Fragment f = op.fragment;if (f != null) {f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition),mTransitionStyle);}switch (op.cmd) {case OP_ADD:f.setNextAnim(op.popExitAnim);mManager.removeFragment(f);break;case OP_REMOVE:f.setNextAnim(op.popEnterAnim);mManager.addFragment(f, false);break;case OP_HIDE:f.setNextAnim(op.popEnterAnim);mManager.showFragment(f);break;case OP_SHOW:f.setNextAnim(op.popExitAnim);mManager.hideFragment(f);break;case OP_DETACH:f.setNextAnim(op.popEnterAnim);mManager.attachFragment(f);break;case OP_ATTACH:f.setNextAnim(op.popExitAnim);mManager.detachFragment(f);break;

看下OP_ATTCH是怎么加进去的

    public FragmentTransaction attach(Fragment fragment) {addOp(new Op(OP_ATTACH, fragment));return this;}void addOp(Op op) {mOps.add(op);op.enterAnim = mEnterAnim;op.exitAnim = mExitAnim;op.popEnterAnim = mPopEnterAnim;op.popExitAnim = mPopExitAnim;}

流程理不顺了,后续打个堆栈看下。

 

 

2.3 restoreAllState

restoreAllState看起来是个恢复机制

    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {// If there is no saved state at all, then there can not be// any nonConfig fragments either, so that is that.if (state == null) return;FragmentManagerState fms = (FragmentManagerState)state;if (fms.mActive == null) return;List<FragmentManagerNonConfig> childNonConfigs = null;// First re-attach any non-config instances we are retaining back// to their saved state, so we don't try to instantiate them again.if (nonConfig != null) {List<Fragment> nonConfigFragments = nonConfig.getFragments();childNonConfigs = nonConfig.getChildNonConfigs();final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;for (int i = 0; i < count; i++) {Fragment f = nonConfigFragments.get(i);if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);int index = 0; // index of f in fms.mActivewhile (index < fms.mActive.length && fms.mActive[index].mIndex != f.mIndex) {index++;}if (index == fms.mActive.length) {throwException(new IllegalStateException("Could not find active fragment "+ "with index " + f.mIndex));}FragmentState fs = fms.mActive[index];fs.mInstance = f;f.mSavedViewState = null;f.mBackStackNesting = 0;f.mInLayout = false;f.mAdded = false;f.mTarget = null;if (fs.mSavedFragmentState != null) {fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(FragmentManagerImpl.VIEW_STATE_TAG);f.mSavedFragmentState = fs.mSavedFragmentState;}}}// Build the full list of active fragments, instantiating them from// their saved state.mActive = new SparseArray<>(fms.mActive.length);for (int i=0; i<fms.mActive.length; i++) {FragmentState fs = fms.mActive[i];if (fs != null) {FragmentManagerNonConfig childNonConfig = null;if (childNonConfigs != null && i < childNonConfigs.size()) {childNonConfig = childNonConfigs.get(i);}Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);mActive.put(f.mIndex, f);// Now that the fragment is instantiated (or came from being// retained above), clear mInstance in case we end up re-restoring// from this FragmentState again.fs.mInstance = null;}}// Update the target of all retained fragments.if (nonConfig != null) {List<Fragment> nonConfigFragments = nonConfig.getFragments();final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;for (int i = 0; i < count; i++) {Fragment f = nonConfigFragments.get(i);if (f.mTargetIndex >= 0) {f.mTarget = mActive.get(f.mTargetIndex);if (f.mTarget == null) {Log.w(TAG, "Re-attaching retained fragment " + f+ " target no longer exists: " + f.mTargetIndex);f.mTarget = null;}}}}// Build the list of currently added fragments.mAdded.clear();if (fms.mAdded != null) {for (int i=0; i<fms.mAdded.length; i++) {Fragment f = mActive.get(fms.mAdded[i]);if (f == null) {throwException(new IllegalStateException("No instantiated fragment for index #" + fms.mAdded[i]));}f.mAdded = true;if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);if (mAdded.contains(f)) {throw new IllegalStateException("Already added!");}synchronized (mAdded) {mAdded.add(f);}}}// Build the back stack.if (fms.mBackStack != null) {mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);for (int i=0; i<fms.mBackStack.length; i++) {BackStackRecord bse = fms.mBackStack[i].instantiate(this);if (DEBUG) {Log.v(TAG, "restoreAllState: back stack #" + i+ " (index " + bse.mIndex + "): " + bse);LogWriter logw = new LogWriter(Log.VERBOSE, TAG);PrintWriter pw = new FastPrintWriter(logw, false, 1024);bse.dump("  ", pw, false);pw.flush();}mBackStack.add(bse);if (bse.mIndex >= 0) {setBackStackIndex(bse.mIndex, bse);}}} else {mBackStack = null;}if (fms.mPrimaryNavActiveIndex >= 0) {mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex);}mNextFragmentIndex = fms.mNextFragmentIndex;}

其中和added相关的就是将缓存的mAdded集合中的fragment清空,从state中重新获取加入

FragmentManagerState fms = (FragmentManagerState)state; 
...     // Build the list of currently added fragments.mAdded.clear();if (fms.mAdded != null) {for (int i=0; i<fms.mAdded.length; i++) {Fragment f = mActive.get(fms.mAdded[i]);if (f == null) {throwException(new IllegalStateException("No instantiated fragment for index #" + fms.mAdded[i]));}f.mAdded = true;if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);if (mAdded.contains(f)) {throw new IllegalStateException("Already added!");}synchronized (mAdded) {mAdded.add(f);}}}

 

3.总结

本地梳理只是粗浅的找了下大概代码位置,待后续打印堆栈继续梳理。

FragmentManager共有3个方法会将isAdded变为true

  • addFragment
  • attachFragment
  • restoreAllState

 

这篇关于(一百三十七)梳理fragment的isAdded的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511392

相关文章

梳理2024年,螺丝钉们爱用的3款剪辑软件

这年头,视频到处都是,就跟天上的星星一样数不清。不管你是公司里的新面孔,还是职场上的老狐狸,学会怎么剪视频,就好比找到了赢的秘诀。不管是给上司汇报工作,展示你的产品,还是自己搞点小视频记录生活,只要是剪辑得漂亮,肯定能一下子吸引大家的目光,让人记得你。咱们今天就来侃侃现在超火的三款视频剪辑工具,尤其是PR剪辑,你肯定听说过,这货在剪辑界可是大名鼎鼎,用它剪视频,既专业又麻利。 NO1. 福昕轻松

ViewPager+fragment实现切换页面(一)

如今的很多应用中都是下面有一排按钮,点击可以切换页面,滑动也可以切换页面。下面就来简单的实现这个功能。 思路 首先肯定是会用到viewpager这个控件,为了能够向下兼容,最好用v4包下的viewpager,Activity要继承FragmentActivity 其次用一个集合来存储所有的fragment页面在设置viewpager的适配器时,把存储fragment页面的list集合传入ada

安卓中的fragment与viewPager的使用问题的解决

最近使用viewPager,结合fragment使用,发现fragment的周期将不符合他的原有的周期流程,多个fragment将会产生错位问题。       通过研究相关代码,发现fragment的切换实际调用的函数为setUserVisibleHint,通过在其中重写方法,将会达到fragment正确切换的效果。       public void setUserVisible

C语言程序设计 笔记代码梳理 重制版

前言 本篇以笔记为主的C语言详解,全篇一共十章内容,会持续更新基础内容,争取做到更详细。多一句没有,少一句不行!  形而上学者谓之道,形而下学者谓之器 形而上学者谓之道,形而下学者谓之器 第1章 C语言的流程 1.C程序经历的六个阶段 编辑(Edit)预处理(Preprocess)编译(Compile)汇编(Assemble)链接(Link)执行(Execute)  2.

0906作业+思维导图梳理

一、作业: 1、创捷一个类似于qq登录的界面 1)源代码 #include "widget.h"#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);//QPushbutton:登录、退出this->join = new QP

简单梳理UE4的Houdini官方插件代码

前言 Houdini官方插件名字叫 “Houdini Engine”,它搭建了Houdini数据与UE4数据间的桥梁。我接触这个插件已经有段时间了,我想是时候梳理一下插件的结构了。(当前我用的UE4版本是4.24.2,Houdini版本18.0.348) 需要说明的是,这篇博客主要是从代码出发的。我准备先分析插件整体的代码结构,再逐个翻阅每个文件试图搞明白他角色。但如果不准备研究代码结构和实现

梳理轻量级建模软件Silo中的所有操作(2):修改

前言 修改(Modify)类操作是建模时使用最为频繁的操作,因此几乎每个操作都分配了快捷键。 本篇包含的快捷键有27个: 操作快捷键追加面(P)追加边(Shift+E)倒角(B)布尔减(,)布尔合(Ctrl+,)布尔交(Shift+<)分离(Ctrl+B)桥接(Shift+B)割裂(X)挤出(Z)补洞(Shift+F)拍扁(Alt+Shift+F)插入型缩放(I)局部缩放(Ctrl+E)局部

Activity被回收导致其内部的Fragment

方式一:   MainActivity重写onSaveInstanceState方法,将super.onSaveInstanceState(outState);注释掉,让其不再保存Fragment的状态,达到其随着MainActivity一起被回收的效果! 方式二: @Overrideprotected void onSaveInstanceState(Bundle outSta

Java学习Day37:HTML 第六章:黄金国(项目思路梳理)

第一天:后端思路梳理及代码实现 1.数据库设计 数据库设计使用一张表(course)设计 CREATE TABLE course(id INT PRIMARY KEY AUTO_INCREMENT COMMENT '课程ID',cname VARCHAR(255) NOT NULL COMMENT '课程名称',price DOUBLE NOT NULL COMMENT '售卖价格

【Linux】常见指令及权限相关知识详细梳理

1.Linux基本指令 1. ls指令         语法: ls [选项][目录或文件]         功能:对于目录,该命令列出该目录下的所有子目录与文件。                    对于文件,将列出文件名以及其他信息。         常用选项:         -a 列出目录下的所有文件,包括以 . 开头的隐含文件。         -d 将目录象文件一样显示,而不