文章目录
首先看看
transaction
中的几个方法:
replace
: 显示和隐藏fragemnt
add
: 将一个Fragment
添加到Activity
中hidden
和show
:显示和隐藏fragemnt
onResume和onPause应用技巧
这两个函数只是跟随activity
的生命周期,并不跟随着fragment
的生命周期,通常应该避免在Activity
的onResume
中进行网络请求或其他逻辑,应为当App
在进行前后台切换的时候,onResume
均会调用。
显示和隐藏Fragment
时,可以采用show
和hidden
组合,也可以使用replace
。
//针对replace的处理方法是使用setUserVisibleHint
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
//相当于Fragment的onResume
} else {
//相当于Fragment的onPause
}
}
//而针对hide/show处理方法是使用onHiddenChanged
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if(hidden){
//TODO now visible to user
} else {
//TODO now invisible to user
}
}
Fragment重叠
为什么会出现界面重叠呢?
当我们离开Activity的时候,切换到别的APP的时候,当内存不够用,Fragment所在Activity被销毁,会调用onSaveInstanceState()方法,Fragment都会被保存起来,当我再次回到这个app的时候,通过onCreate中的参数savedInstanceState恢复了之前的fragment,就导致了界面重叠。
replace
使用replace
方式,虽然这种方式会避免上述的bug
,但也是重复创建了对象,每次replace
会把生命周期全部执行一遍,它会销毁视图,重新加载,如果在这些生命周期函数 里拉取数据的话,就会不断重复的加载刷新数据。
add [show, hidden]
- 在
add
的时候,加上一个tag
参数
transaction.add(R.id.content, IndexFragment,”Tab1″);
- 然后当
IndexFragment
引用被回收置空的话,先通过
IndexFragment=FragmentManager.findFragmentByTag(“Tab1″);
找到对应的引用,然后继续上面的hide
,show
;
Fragment中getActivity()的空指针问题
这个问题的原因大部分在于Fragment已经和所在activity解除了关联
,也就是调用了onDetach()
方法。
解决方法:
我们可以在Fragment
中定义Activity
成员变量,当Activity
和Fragment
关联的时候,给Activity
赋值,这样即使Activity
和Fragment
解除关联后,成员变量的地址任然指向Activity
。
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity)context;
}
Fragment State Loss
Fragment
的作用域是Activity
,FragmentManager
管理着一个Activity
所有的Fragment
,这些Fragment
被放入一个栈中。每个Fragment
有一个FragmentState
,它相当于Fragment
的snapshot
,保存状态时FragmentManager
把每个Fragment
的FragmentState
存储起来,最终存储到Activity
的savedInstanceState
中。
既然状态的保存与恢复都必须要把Fragment
带上,那么一旦当Fragment
的状态已保存过了,那么就不应该再改变Fragment
的状态。因此FragmentManager
的每一个操作前,都会调用一个方法来检查状态是否保存过了:
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
原因就是commit
操作发生在了状态保存之后,大招就是使用commitAllowStateLoss
。
Activity#onBackPressed
还有一种情况,也会出现此异常,而且是在Activity中完全 没有Fragment的情况下,所以解决办法是:同样设置一个标志,如果状态已保存过,就不要再处理onBackPressed。
public class FragmentStateLossActivity extends Activity {
private static final String TAG = "Fragment state loss";
private boolean mStateSaved;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_state_loss);
mStateSaved = false;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Not call super won't help us, still get crash
super.onSaveInstanceState(outState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mStateSaved = true;
}
}
@Override
protected void onResume() {
super.onResume();
mStateSaved = false;
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStop() {
super.onStop();
mStateSaved = true;
}
@Override
protected void onStart() {
super.onStart();
mStateSaved = false;
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (!mStateSaved) {
return super.onKeyDown(keyCode, event);
} else {
// State already saved, so ignore the event
return true;
}
}
@Override
public void onBackPressed() {
if (!mStateSaved) {
super.onBackPressed();
}
}
}
参考链接:
你真的会用 Fragment 了么?-Fragment 解析
让你不再俱怕 Fragment State Loss
Fragment销毁时replace和add两个方法的区别