setUserVisibleHint(false) > onAttach > onCreate > setUserVisibleHint(false) >onCreateView > onViewCreated > onActivityCreated > onStart > onResume > setUserVisibleHint(false) > onPause > onStop > onDestroyView > onDestroy > onDetach
setUserVisibleHint反映出Fragment是否被切换到后台或前台,在ViewPager中存在预加载机制,对用户不可见的Fragment可能也加载了并onResume,所以不能通过onResume来判定Fragment的可见性来进行延迟加载的操作,而是利用setUserVisiableHint。
/**
* Fragment初始化时,在onAttach之前也会回调setUserVisibleHint且isVisibleToUser为false,所以加个判断,避免初始化时执行setUserVisibleHint中的逻辑。
*/
//判断是否是初始化Fragment。
private boolean hasStarted = false;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
L.v(TAG, "setUserVisibleHint " + isVisibleToUser);
if (isVisibleToUser) {
hasStarted = true;
L.v(TAG,"开始界面");
} else {
if (hasStarted) {
L.v(TAG,"结束界面");
}
}
}
使用hide和show这时fragment的生命周期不再执行,不走任何的生命周期,这样在有的情况下,数据将无法通过生命周期方法如onResume进行刷新,所以你可以使用onHiddenChanged方法来解决这问题。
Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) { // 不在最前端显示 相当于调用了onPause();
return;
}else{ // 在最前端显示 相当于调用了onResume();
//网络数据刷新
}
}
开发中我们会遇到在Fragment中通过startaActivity启动一个目标Activity,当目标Activity结束并setResult,在Fragment的onActivityResult方法中收不到返回的数据。要避免这个问题,需要注意以下两点:
1.在Fragment中使用startActivityForResult的时候,不要使用getActivity().startActivityForResult,而是应该直接使startActivityForResult()。
2.如果activity中重写了onActivityResult,那么activity中的onActivityResult一定要加上super.onActivityResult(requestCode, resultCode, data)。
如果疏忽以上两点,数据只能传到宿主Activity的onActivityResult。
在Fragment多层嵌套的场景下,需要在宿主Activity的onActivityResult中手动将结果传到所有附属Fragment。
public class MyBaseFragmentActivity extends FragmentActivity
{
private String TAG="MyBaseFragmentActivity";
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
FragmentManager fragmentManager=getSupportFragmentManager();
for(int indext=0;indext//找到第一层Fragment
if(fragment==null)
Log.w(TAG, "Activity result no fragment exists for index: 0x"
+ Integer.toHexString(requestCode));
else
handleResult(fragment,requestCode,resultCode,data);
}
}
/**
* 递归调用,对所有的子Fragment生效
*/
private void handleResult(Fragment fragment,int requestCode,int resultCode,Intent data)
{
fragment.onActivityResult(requestCode, resultCode, data);//调用每个Fragment的onActivityResult
Log.e(TAG, "MyBaseFragmentActivity");
List childFragment = fragment.getChildFragmentManager().getFragments(); //找到第二层Fragment
if(childFragment!=null)
for(Fragment f:childFragment)
if(f!=null)
{
handleResult(f, requestCode, resultCode, data);
}
if(childFragment==null)
Log.e(TAG, "MyBaseFragmentActivity1111");
}
}
值得注意的一点
在所有的Fragment中若是直接使用startActivityForResult(),那么传到activity中的onActivityResult中的requestCode就会不对,resultCode是对的,当然,这样的话传到每个Fragment中的onActivityResult的requestCode也是不对的。若是用getActivity().startActivityForResult,则传出来的requestCode和rusultCode就都是对的。
当然若是直接按返回键返回,那么会自动给一个resultCode,这个resultCode和我们设定的是不一样的,因此在重写onActivityResult的时候需要判定resultCode。
Activity异常退出时,会回调onSaveInstanceState来保存View树中每一个View的状态,这里的View指的是那些实现了View状态保存/恢复的内部方法且提供android:id,除此之外,Activity无法自动保存这些View状态。当onRestoreInstanceState调用时,Activity再将这些状态按android:id返回给对应的View。
Fragment在onSaveInstanceState/onActivityCreated进行状态保存和恢复。
自定义View时,一定要重写onSaveInstanceState/onRestoreInstanceState,这样这种保存和恢复View 状态的机制也能适用于你的View。
基本上来说,每一个单独的Android提供的的标准View 组件都已经在内部完成了这些事情,例如EditText,TextView,Checkbox.但是需要你手动让它生效,例如,你需要为TextView设置android:freezeText为true,来使用这个功能。
Activity+Fragment组合使用时,由于这个保存/恢复机制会产生很多的坑
1. Activity常驻后台,被回收,但Fragment被保存,当Activity重建时,Fragment恢复,但Fragment的宿主Activity已经销毁了,导致getActivity为空。
解决方法:让Fragment随宿主Activity一起销毁
@Override
protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState); }
其实这个坑还有个有用的地方。Activity异常销毁时,onSaveInstanceState能保存的数据有限,数据过大容易oom。所以我们可以在onSaveInstanceState时attach一个Fragment,并将要存储的大数据保存到这个Fragment。当Activity重建时找到这个Fragment,再从中取出那些数据。
Fragmentation,完美填补Fragment嵌套时或者单Activity+多Fragment架构时遇到的坑。
https://github.com/YoKeyword/Fragmentation
Icepick,Activity/Fragment状态保存
https://github.com/frankiesardo/icepick