Fragment踩坑总结

生命周期

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,"结束界面");  
            }  
        }  
    } 

onHiddenChanged

使用hide和show这时fragment的生命周期不再执行,不走任何的生命周期,这样在有的情况下,数据将无法通过生命周期方法如onResume进行刷新,所以你可以使用onHiddenChanged方法来解决这问题。

Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (hidden) {   // 不在最前端显示 相当于调用了onPause();
            return;
        }else{  // 在最前端显示 相当于调用了onResume();
           //网络数据刷新
        }
    }

onActivityResult

开发中我们会遇到在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); }

  1. Activity onCreate中添加Fragment,先判断onCreate中的savedInstanceState是否为空,为空时再进行添加,防止重复添加,出现界面重叠。

其实这个坑还有个有用的地方。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

你可能感兴趣的:(Android)