当前位置:首页 » 操作系统 » fragment源码分析

fragment源码分析

发布时间: 2024-08-23 23:41:38

❶ 从源码角度分析,为什么会发生 Fragment 重叠

1、发生了页面重启(旋转屏幕、内存不足等情况被强杀重启)。2、使用add()方式加载Fragment;
为什么会发生Fragment重叠?
从源码角度分析,为什么发生页面重启后会导致重叠?(在以add方式加载Fragment的时候)
我们知道Activity中有个onSaveInstanceState()方法,该方法在app进入后台、屏幕旋转前、跳转下一个
Activity等情况下会被调用,此时系统帮我们保存一个Bundle类型的数据,我们可以根据自己的需求,手动保存一些例如播放进度等数据,而后如果
发生了页面重启,我们可以在onRestoreInstanceState()或onCreate()里get该数据,从而恢复播放进度等状态。

而产生Fragment重叠的原因就与这个保存状态的机制有关,大致原因就是系统在页面重启前,帮我们保存了Fragment的状态,但是在重启后恢复时,视图的可见状态没帮我们保存,而Fragment默认的是show状态,所以产生了Fragment重叠现象。

分析: 我们先看FragmentActivity的相关源码:
public class FragmentActivity extends ... {
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

protected void onCreate(@Nullable Bundle savedInstanceState) {
...省略
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
}
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable p = mFragments.saveAllState();
...省略
}
}
从上面源码可以看出,FragmentActivity确实是帮我们保存了Fragment的状态,并且在页面重启后会帮我们恢复!

其中的mFragments是FragmentController,它是一个Controller,内部通过FragmentHostCallback间接控制FragmentManagerImpl。相关代码如下:
public class FragmentController {
private final FragmentHostCallback<?> mHost;

public Parcelable saveAllState() {
return mHost.mFragmentManager.saveAllState();
}

public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
mHost.mFragmentManager.restoreAllState(state, nonConfigList);
}
}

public abstract class FragmentHostCallback<E> extends FragmentContainer {
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
}
通过上面代码可以看出FragmentController通过FragmentHostCallback里的FragmentManagerImpl对象来控制恢复工作。

我们接着看FragmentManagerImpl到底做了什么:
final class FragmentManagerImpl extends FragmentManager {
Parcelable saveAllState() {
...省略 详细保存过程
FragmentManagerState fms = new FragmentManagerState();
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
return fms;
}

void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
// 恢复核心代码
FragmentManagerState fms = (FragmentManagerState)state;
FragmentState fs = fms.mActive[i];
if (fs != null) {
Fragment f = fs.instantiate(mHost, mParent);

}
}
我们通过saveAllState()看到了关键的保存代码,原来是是通过FragmentManagerState来保存Fragment的状态、所处Fragment栈下标、回退栈状态等。

而在restoreAllState()恢复时,通过FragmentManagerState里的FragmentState的instantiate()方法恢复了Fragment(见下面的分析就明白啦)

我们看下FragmentManagerState:
final class FragmentManagerState implements Parcelable {
FragmentState[] mActive; // Fragment状态
int[] mAdded; // 所处Fragment栈下标
BackStackState[] mBackStack; // 回退栈状态
...
}
我们只看FragmentState,它也实现了Parcelable,保存了Fragment的类名、下标、id、Tag、ContainerId以及Arguments等数据:
final class FragmentState implements Parcelable {
final String mClassName;
final int mIndex;
final boolean mFromLayout;
final int mFragmentId;
final int mContainerId;
final String mTag;
final boolean mRetainInstance;
final boolean mDetached;
final Bundle mArguments;
...

// 在FragmentManagerImpl的restoreAllState()里被调用
public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
...省略
mInstance = Fragment.instantiate(context, mClassName, mArguments);
}
}
至此,我们就明白了系统帮我们保存的Fragment其实最终是以FragmentState形式存在的。

此时我们再思考下为什么在页面重启后会发生Fragment的重叠? 其实答案已经很明显了,根据上面的源码分析,我们会发现FragmentState里没有Hidden状态的字段!

而Hidden状态对应Fragment中的mHidden,该值默认false...
public class Fragment ... {
boolean mHidden;
}
我想你应该明白了,在以add方式加载Fragment的场景下,系统在恢复Fragment时,mHidden=false,即show状态,这
样在页面重启后,Activity内的Fragment都是以show状态显示的,而如果你不进行处理,那么就会发生Fragment重叠现象!
为什么使用add()加载Fragment会导致重叠?
我们知道加载Fragment有2种方式:replace()和add()。使用replace加载Fragment是不会发生重叠现象的,只有通过add方式才有可能发生重叠现象。

我们一般使用add时,会和show(),hide()配合使用,add配合hide是使Fragment的视图改变为GONE状态;而
replace是销毁Fragment
的视图。页面重启时,add的Fragment会全部走生命周期,创建视图;而replace的非栈顶Fragment不会走生命周期,只有Back时,
才会逐一走栈顶Fragment生命周期,创建视图。

结合上面的源码分析,在使用replace加载Fragment时,页面重启后,Fragment视图都还没创建,所以mHidden没有意义,不
会发生重叠现象;而在使用add加载时,视图是存在的并且叠加在一起,页面重启后
mHidden=false,所有的Fragment都会是show状态显示出来(即VISIBLE),从而造成了Fragment重叠!

❷ Android组件之Fragment(一)---基础知识与运用

Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment, 我们可以把他看成一个小型的Activity,又称Activity片段!想想,如果一个很大的界面,我们 就一个布局,写起界面来会有多麻烦,而且如果组件多的话是管理起来也很麻烦!而使用Fragment 我们可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理!从而可以更加方便的在 运行过程中动态地更新Activity的用户界面!另外Fragment并不能单独使用,他需要嵌套在Activity 中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响,比如Activity 被destory销毁了,他也会跟着销毁!

引用官方的一张图片,其实已经说明问题了,就是为了更好的适配大屏,在大屏的时候,不需要去在一个activity内部通过复杂的布局和界面去实现,只需要去在一个activity内部,通过多个fragment来做界面布局实现即可,而且针对于多个fragment来说, 每个fragment有单独的生命周期,

Demo样例,我们在一个界面中,有上下两个fragment,如图所示:

Step 2: Fragment创建,视图加载,数据赋值
BlankFragment .java

Step 3: Activity在onCreate( )方法中调用setContentView()之后调用FragmentTransaction 进行事务提交
FragmentTestActivity.java

在xml中声明两个fragment,指定为具体的fragment

Step 1:定义Fragment的布局,就是fragment显示内容的

Step 2:自定义一个Fragment类,需要继承Fragment或者他的子类,重写onCreateView()方法 在该方法中调用:inflater.inflate()方法加载Fragment的布局文件,接着返回加载的view对象
BlankFragment.java

Step 3:在需要加载Fragment的Activity对应的布局文件中添加fragment的标签, 记住,name属性是全限定类名哦,就是要包含Fragment的包名,如:

Step 4: Activity在onCreate( )方法中调用setContentView()加载布局文件即可!

针对在一个Activity中的某个Layout中切换Fragment,,无非两种方法:

我们自己看一下方法注释

源码方法注释里面说的很明白,这个方法会移除所有的fragment,然后添加当前的fragment。
这时分为两种情况,一种是fragment已有并且在前台展示,一种是未有或者在后台,针对于前者,此时replace,生命周期不会发生变化,针对后者,生命周期会重新走

分为两种情况,一种fragment已存在,一种未存在,针对于前者,生命周期无变化,但是会回调onHiddenChanged方法,针对于后者,生命周期会创建一次。

1.Fragment是Google官方引入的一个为了适配大屏、多页面的一个组件。您可以理解为它就是一个类而已,只不过里面包含了View,并且与activity的生命周期进行了关联。
2.动态加载与静态加载相对来说,建议使用动态加载,静态加载固定在了xml文件中,永远不变。
3.replace的fragment如果不在前台,会执行所有生命周期,反之不会执行任何生命周期方法;hide+show生命周期并不会发生变化,但是会回调onHiddenChanged方法,在实际开发中,建议add之后,使用hide+show来操作fragment,一方面减少资源的重复加载和创建,另外一方面提升用户体验感。
4.fragment的生命周期大体上和activity一致,但是前期和后期多了一些东西,因为fragment内部有view,那么这个view需要进行创建、然后添加到activity内部,所以相应的在onCreate与onStart之间就多了几个方法-onCreateView、onViewCreated。相同的道理,fragment的view与activity解绑,也相应的在onStop与onDestory之间多个方法-onDestroyView。onAttach与onDetach可以理解为视图与activity产生关联和接触关联,是最开始和最后的步骤。

Demo地址

❸ 浅析Fragment为什么需要空的构造方法

因为Activity横竖屏切换的时候,会重新构造Fragment,默认调用的就是Fragment的无参构造方法。
如果通过 setArguments来传递参数的话,Activity在构造Fragment时会通过反射无参构造实例化一个新的Fragment,并且给mArgments初始化为原先的值。
如果参数是通过构造方法传递的,在重新构造Fragment实例之后,数据就丢失了

这有一篇:Fragment源码分析http://blog.csdn.net/shensky711/article/details/53171248,希望可以帮到你

热点内容
罪恶都市安卓内置菜单在哪里下载 发布:2024-11-25 07:09:51 浏览:706
数据库附加数据库 发布:2024-11-25 07:08:08 浏览:403
支付宝支付密码如何修改 发布:2024-11-25 06:38:47 浏览:923
java开发要学习什么技术 发布:2024-11-25 06:20:28 浏览:1000
java猿 发布:2024-11-25 06:18:36 浏览:127
如何刷安卓44 发布:2024-11-25 06:18:32 浏览:529
安卓手机怎么限制app时间 发布:2024-11-25 06:14:15 浏览:403
福建虚拟服务器管理软件云服务器 发布:2024-11-25 06:05:46 浏览:106
android加载图片 发布:2024-11-25 06:05:00 浏览:168
linux的ls 发布:2024-11-25 05:47:56 浏览:845