前言:
先来介绍一个专业术语:
"内存重启":“术语”,安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)
在系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复, Fragment会在宿主Activity的onCreate方法调用后紧接着恢复(从onAttach生命周期开始)。
activity 传递数据给fragment
google推荐使用这种方式
public static final FragmentListView newInstance(String string){
FragmentListView fragmentListView = new FragmentListView();
Bundle bundle = new Bundle();
//将需要传递的字符串以键值对的形式传入bundle
bundle.putString("data",data);
fragmentListView.setArguments(bundle);
return fragmentListView;
}
二、设置传递的参数:
@Override
public void setArguments(Bundle args) {
super.setArguments(args);
}
三、获取传递进来的参数:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle arguments = getArguments();
}
add(), show(), hide(), replace()的那点事
1、区别
show(),hide()最终是让Fragment的View setVisibility(true还是false),不会调用生命周期;
replace()的话会销毁视图,即调用onDestoryView、onCreateView等一系列生命周期;
add()和 replace()不要在同一个阶级的FragmentManager里混搭使用。
2、使用场景
如果你有一个很高的概率会再次使用当前的Fragment,建议使用show(),hide(),可以提高性能。
在我使用Fragment过程中,大部分情况下都是用show(),hide(),而不是replace()。
注意:如果你的app有大量图片,这时更好的方式可能是replace,配合你的图片框架在Fragment视图销毁时,回收其图片所占的内存。
块Activity+多Fragment的架构?
单Activity+多Fragment:
一个app仅有一个Activity,界面皆是Frament,Activity作为app容器使用。
优点:性能高,速度最快。参考:新版知乎 、google系app
缺点:逻辑比较复杂,尤其当Fragment之间联动较多或者嵌套较深时,比较复杂。
多模块Activity+多Fragment:
一个模块用一个Activity,比如
1、登录注册流程:
LoginActivity + 登录Fragment + 注册Fragment + 填写信息Fragment + 忘记密码Fragment
2、或者常见的数据展示流程:
DataActivity + 数据列表Fragment + 数据详情Fragment + ...
优点:速度快,相比较单Activity+多Fragment,更易维护。
权衡利弊,我认为多模块Activity+多Fragment是最合适的架构,开发起来不是很复杂,app的性能又很高效。
当然。Fragment只是官方提供的灵活组件,请优先遵从你的项目设计!真的特别复杂的界面,或者单个Activity就可以完成一个流程的界面,使用Activity可能是更好的方案。
activity与fragment常用的交互的方式:
1. handler:在fragment里面创建handler,再利用activity对象将这个handler对象设置到activity里面
2. brodcast:在activity里面注册广播,在Brodcast里面发送
3. 接口回调:在Brodcast里面创建接口,在activity实现这个接口
4. 下面提到的getSupportFragmentManager().findFragmentByTag()和getSupportFragmentManager().getFragments()
getActivity()空指针问题
不要在fragment里面直接使用getActivity(),用下面代码代替:
@Override
public void onAttach(Context context) {
super.onAttach(context);
activity = (MainActivity) context;
}
Fragment重叠异常-----正确使用hide、show的姿势:
如果你add()了几个Fragment,使用show()、hide()方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。
原因是FragmentManager帮我们管理Fragment,当发生“内存重启”,他会从栈底向栈顶的顺序一次性恢复Fragment;
但是因为没有保存Fragment的mHidden属性,默认为false,即show状态,所以所有Fragment都是以show的形式恢复,我们看到了界面重叠。
(如果是replace,恢复形式和Activity一致,只有当你pop之后上一个Fragment才开始重新恢复,所有使用replace不会造成重叠现象)
还有一种场景,add和replace都有可能造成重叠: 在onCreate中加载Fragment,并且没有判断saveInstanceState==null,导致重复加载了同一个Fragment导致重叠。(PS:replace情况下,如果没有加入回退栈,
则不判断也不会造成重叠,但建议还是统一判断下)
方法一:根据tag来判断
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “内存重启”时调用
targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
// 解决重叠问题
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常时
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, targetFragment, targetFragment.getClass().getName())
.add(R.id,container,hideFragment,hideFragment.getClass().getName())
.hide(hideFragment)
.commit();
}
}
如果你想恢复到用户离开时的那个Fragment的界面,你还需要在onSaveInstanceState(Bundle outState)里保存离开时的那个可见的tag或下标,在onCreate“内存重启”代码块中,取出tag/下标,进行恢复。
方式二:通过getFragments()可以获取到当前FragmentManager管理的栈内所有Fragment。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “内存重启”时调用
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if(fragment instanceof TartgetFragment){
targetFragment = (TargetFragment)fragment;
}else if(fragment instanceof HideFragment){
hideFragment = (HideFragment)fragment;
}
}
// 解决重叠问题
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常时
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
// 这里add时,tag可传可不传
getFragmentManager().beginTransaction()
.add(R.id.container)
.add(R.id,container,hideFragment)
.hide(hideFragment)
.commit();
}
}