Android 基础之 Fragment 面面观

作者: 秀花123 | 来源:发表于2017-05-16 22:49 被阅读101次

    一、生命周期

    Activity/Fragment 生命周期

    上图是 Fragment 和 Activity 比较完整的生命周期函数回调,相对于 Activity,Fragment 也有自己独立的生命周期。
    Activity 的七个生命周期回调函数除了 onRestart,Fragment 都有,意义类似于 Activity,此外还多了五个回调函数:

    1. onAttach(Activity):Fragment 和 Activity 发生关联时调用。当 Fragment 从 layout 文件中加载出来或者在 Activity 中调用 FragmentManager#add 方法之后第一个调用的回调函数;
    2. onCreate:
    3. onCreateView(LayoutInflater,ViewGroup,Bundle):创建 Fragment 的 View;
    4. onActivityCreated(Bundle):Activity#onCreate 方法返回时调用;
    5. onStart:
    6. onResume:
    7. onPause:
    8. onStop:
    9. onDestroyView():Fragment 被移除时销毁 View;
    10. onDestroy:
    11. onDetach():Fragment 和 Activity 解除关联;

    一些场景的生命周期:

    • add:onAttach --> onCreate --> onCreateView --> onActivityCreated --> onStart --> onResume
    • remove:onPause --> onStop --> onDestroyView --> onDestroy --> onDetach
    • replace:实质上是调用 remove 方法和 add 方法
    • hide:不执行任何生命周期函数
    • show:不执行任何生命周期函数
    • remove 或 replace 后 addToBackStack:不执行 onDestroy 和 onDetach 方法
    • remove 或 replace 后从 BackStack 返回:不执行 onAttach 和 onCreate 方法
    • 锁屏、弹出 DIalog、Home 键回主屏等类似于 Activity

    二、使用

    静态使用

    在布局文件中作为一个普通控件添加

    动态使用

    通过 FragmentManager 操作 FragmentTransition 执行 add、remove、replace、hide、show 等一系列操作

    replace 和 hide 的区别:
    hide 和 show 只是显示和隐藏,不会回调生命周期函数,可以通过
    onHiddenChanged 函数执行一些操作,而 replace 方法不会保存 Fragment 的状态。

    addToBackStack:添加事务到回退栈,按返回键时会返回当前事务
    当 Fragment 嵌套时需要使用 getChildFragmentManager 来获取 FragmentManager

    三、状态保存恢复

    当按 Home 键回主屏、锁屏等操作使 Fragment 回调了 onPause 方法,而非主动调用 replace、remove 等方法,Fragment 的 onSaveInstanceState 方法会执行对 View 进行状态保存,当系统资源不足杀死 Fragment 后,在 onActivityCreated 方法中进行状态恢复,如果 View 都实现了保存和恢复状态的方法,那么只需要对成员变量的状态进行恢复和保存。
    如果 remove 或 replace 之后,事务加入了回退栈,会调用 onDestroyView 销毁 Fragment 的 View,在从回退栈返回时,Fragment 内部会调用 View 的状态保存和恢复方法进行处理,而 Fragment 的 onDestroy 方法没有被回调,Fragment 的实例还存在,Fragment 的成员变量也存在,这种情况下,不需要作任何操作来保存和恢复状态。

    四、getActivity()

    1、getActivity() 为 null

    这种情况多数是因为 Fragment 已经和 Activity 解除了关联,宿主 Activity 已销毁。
    解决方法:在异步回调是进行判空(fragment.isAdd(),getActivity()!=null 等),或者在 Fragment 实例创建时就通过
    getActivity().getApplicationContext() 方法保存整个应用的上下文对象

    2、内存泄漏。如果 Fragment 持有宿主 Activity 的引用,会导致宿主 Activity 无法回收,造成内存泄漏。

    解决方法:在 onAttach(Context) 方法中获取 Context 对象

    五、Fragment 重叠

    当 Activity 销毁重建时,会恢复其关联的 Fragment,而 Fragment 实例是自身也会销毁重建,这个时候就会造成 Fragment 重叠。
    解决方案:在 Activity 创建 Fragment 实例时进行判断:

    1. 在 Activity#onAttachFragment 时判断:
    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment instanceof  OneFragment){
            oneFragment = (OneFragment) fragment;
        }
    }
    
    1. 在创建 Fragment 前添加判断,判断是否已经存在:
    Fragment tempFragment = getSupportFragmentManager().findFragmentByTag("OneFragment");
    if (tempFragment==null) {
        oneFragment = OneFragment.newInstance();
        ft.add(R.id.fl_content, oneFragment, "OneFragment");
    }else {
        oneFragment = (OneFragment) tempFragment;
    }
    
    1. 直接利用 savedInstanceState 判断即可:
    if (savedInstanceState==null) {
        oneFragment = OneFragment.newInstance();
        ft.add(R.id.fl_content, oneFragment, "OneFragment");
    }else {
        oneFragment = (OneFragment) getSupportFragmentManager().findFragmentByTag("OneFragment");
    }
    

    六、懒加载

    典型代码:

    public abstract class LazyFragment extends Fragment {
    
        private boolean isVisible;
        private boolean isViewCreated;
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View view = inflater.inflate(getLayoutID(), container, false);
            Log.e("blink","onCreateView");
            isViewCreated = true;
            return view;
        }
    
        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            if(getUserVisibleHint()) {
                isVisible = true;
                loadForData();
            } else {
                isVisible = false;
                onInvisible();
            }
            if (isViewCreated && isVisible)
                loadForUI();
        }
    
        protected abstract void onInvisible();
    
        protected abstract void loadForData();
    
        protected abstract void loadForUI();
    
        // provide layout id
        public abstract int getLayoutID();
    
    }
    

    需要显式调用 setUserVisibleHint(true)

    七、通信

    Fragment 之间通信

    1. 使用 setArguments,getArguments,参数是一个 Bundle 对象;
    2. 回调函数;

    Activity 和 Fragment 通信

    1. getActivity 获取 Activity 的引用;
    2. Activity 持有 Fargment 实例的引用;
    3. 回调函数;

    相关文章

      网友评论

        本文标题:Android 基础之 Fragment 面面观

        本文链接:https://www.haomeiwen.com/subject/sftntxtx.html