浅析 Fragment 回退栈

    在我们使用 fragment 的时候 ,总是会使用到 fragmentTransaction 的 add remove 和 replace 方法, 这些方法对 fragment 生命周期有着不同的影响, 在来个 回退栈, 就更加容易混淆.

    我们通过开启回退栈和关闭回退栈来分别查看 fragment 的生命周期来了解 fragment 回退栈对其生命周期的影响.


不使用任何回退栈, 代码如下:

private void initialize() {
 
        FirstFragment firstFragment = FirstFragment.newInstance("1", "2");
        SecondFragment secondFragment = SecondFragment.newInstance("3", "4");
 
        firstFragmentBtn.setOnClickListener((View v) -> {
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.add(R.id.frame_layout, firstFragment);
//            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.commit();
        });
 
        secondFragmentBtn.setOnClickListener(v -> {
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.add(R.id.frame_layout, secondFragment);
//            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.commit();
        });
 
        backBtn.setOnClickListener(v -> {
//            getSupportFragmentManager().popBackStack();
            getSupportFragmentManager().beginTransaction().remove(secondFragment).commit();
        });
 
    }

    我们通过简单的 add 方法一直往 frameLayout 中添加 fragment, 其效果就是一直把 fragment 的视图叠加到 frameLayout 上面, 下层视图不会移除.

    日志如下:

-------- 点击第一个按钮 --------
 
D/FragmentFirst--->onAttach======: first
D/FirstFragment--->onCreate======: first
D/FirstFragment--->onCreateView======: first   地址:@118390022
D/FirstFragment--->onActivityCreated======: first
 
-------- 点击第二个按钮 --------
 
D/SecondFragment--->onAttach======: second
D/SecondFragment--->onCreate======: second
D/SecondFragment--->onCreateView======: second   地址:@34476387
D/SecondFragment--->onActivityCreated======: second
 
-------- 点击回退按钮 --------
 
D/SecondFragment--->onPause======: second
D/SecondFragment--->onStop======: second
D/SecondFragment--->onDestroyView======: second
D/SecondFragment--->onDestroy======: second
D/SecondFragment--->onDetach======: second
 
-------- 再点击第二个按钮 --------
 
D/SecondFragment--->onAttach======: second
D/SecondFragment--->onCreate======: second
D/SecondFragment--->onCreateView======: second   地址:@34476387
D/SecondFragment--->onActivityCreated======: second

    从日志中我们可以看到, 当没有添加到回退栈时, remove 一个 fragment 会调用到 onDetach 方法, 如果我们把第二个按钮的 add 改为 replace .

     日志如下 :

-------- 点击第一个按钮 -------
 
D/FragmentFirst--->onAttach======: first
D/FirstFragment--->onCreate======: first
D/FirstFragment--->onCreateView======: first   地址:@118390022
D/FirstFragment--->onActivityCreated======: first
 
-------- 点击第二个按钮 --------
 
D/SecondFragment--->onAttach======: second
D/SecondFragment--->onCreate======: second
 
D/FirstFragment--->onPause======: first
D/FirstFragment--->onStop======: first
D/FirstFragment--->onDestroyView======: first
D/FirstFragment--->onDestroy======: first
D/FirstFragment--->onDetach======: first
 
D/SecondFragment--->onCreateView======: second   地址:@34476387
D/SecondFragment--->onActivityCreated======: second

    我们可以看到, replace 会先把第一个 fragment 移除, 调用到 onDetach 方法, 然后再添加第二个, 所以说 replace 是 remove 和 add 的集合.


开启回退栈:

代码:

private void initialize() {
 
        FirstFragment firstFragment = FirstFragment.newInstance("1", "2");
        SecondFragment secondFragment = SecondFragment.newInstance("3", "4");
 
        firstFragmentBtn.setOnClickListener((View v) -> {
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.add(R.id.frame_layout, firstFragment);
            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.commit();
        });
 
        secondFragmentBtn.setOnClickListener(v -> {
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.frame_layout, secondFragment); // 注意此处为 replace, 如果为 add 则依然是叠加效果, 看不到回退栈的意义
            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.commit();
        });
 
        backBtn.setOnClickListener(v -> {
            getSupportFragmentManager().popBackStack();
//            getSupportFragmentManager().beginTransaction().remove(secondFragment).commit();
        });
    }

    日志:

-------- 点击第一个按钮 --------
 
D/FragmentFirst--->onAttach======: first
D/FirstFragment--->onCreate======: first
D/FirstFragment--->onCreateView======: first   地址:@118390022
D/FirstFragment--->onActivityCreated======: first
 
-------- 点击第二个按钮 --------
 
D/SecondFragment--->onAttach======: second
D/SecondFragment--->onCreate======: second
 
D/FirstFragment--->onPause======: first
D/FirstFragment--->onStop======: first
D/FirstFragment--->onDestroyView======: first
 
D/SecondFragment--->onCreateView======: second   地址:@34476387
D/SecondFragment--->onActivityCreated======: second
 
-------- 回退一次 --------
 
D/SecondFragment--->onPause======: second
D/SecondFragment--->onStop======: second
D/SecondFragment--->onDestroyView======: second
D/SecondFragment--->onDestroy======: second
D/SecondFragment--->onDetach======: second
 
D/FirstFragment--->onCreateView======: first   地址:@118390022
D/FirstFragment--->onActivityCreated======: first
 
-------- 回退第二次 --------
 
D/FirstFragment--->onPause======: first
D/FirstFragment--->onStop======: first
D/FirstFragment--->onDestroyView======: first
D/FirstFragment--->onDestroy======: first
D/FirstFragment--->onDetach======: first

    我们可以看到, 如果添加了回退栈, 当我们使用 replace 方法时, 之前的 fragment 不会调用到 onDetach 方法, 只销毁了其 view, 然后我们进行一次回退操作, 可以看到 fragment 2 正常被销毁, 调用到了 onDetach 方法, 此时 fragment 1 会重新出显示, 如果没有采用回退栈, 用 replace 方法替换之后, 之前的 fragment 1 是不会显示的, 因为其已经和 activity 接触了关联. 然后我们进行第二次回退操作, 同样的是 fragment 1 被销毁.

    在这里有个很歧义的地方, 笔者通过多次打日志查看地址发现, 当一个 fragment 调用到 onDetach 方法之后, 其实列对象并没有销毁重建, 其地址还是一样的, 我们依然可以把这个实例对象重新添加到容器中和 activity 建立关联, 所以不太理解为什么那么多博客都说到调用到 onDetach 之后, fragment 会销毁, 实例会重新创建.

     但是笔者认为,其实并不是这样, 销毁一个 fragment ,只是和 activity 解除了关联, 比调用到 onDestoryView 多调用了几个方法罢了, 其实例对象并没有得到重新创建.


    通过上面的方法我们发现无论如何都会销毁其视图状态重新创建视图, 这样做固然有好处,可以节省很多视图占用的内存, 但是存在一个很致命的地方, 那就是用户的视图状态得不到保存, 比如 recyclerView 滑动的位置. 因此如果我们想要保存其视图状态, 可以使用 hide 方法, 并且在 onHiddenChange 方法中监听当前 fragment 是否隐藏.

    此外, 如果使用回退栈, 可以通过使用 返回 按钮来进行 fragment 的回退.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值