解决Activity意外销毁重建后切换Fragment显示重叠问题

1. 问题现象

在之前开发的一款应用中,有用户反映将App长期置于后台(20-30min,小米Mix2)后返回,在首页切换Fragment,有时会出现Fragment重叠显示的问题,示意图如下:
正常情况:
正常情况
异常情况:
异常情况
离开时用户停留在第三个Fragment,返回时TabLayout选择的是第一个Fragment,第一个和第三Fragment同时显示,并且此时切换Fragment,第三个Fragment并不会隐藏。
显示和隐藏Fragment的代码如下:

	private void showFragmentByPosition(int position) {
        hideAllFragment();
        switch (position) {
            case INDEX_ONE:
                showOneFragment();
                break;
            case INDEX_TWO:
                showTwoFragment();
                break;
            case INDEX_THREE:
                showThreeFragment();
                break;
            default:
                break;
        }
    }

    private void hideAllFragment() {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        if (oneFragment != null) {
            transaction.hide(oneFragment);
        }

        if (twoFragment != null) {
            transaction.hide(twoFragment);
        }

        if (threeFragment != null) {
            transaction.hide(threeFragment);
        }

        transaction.commitAllowingStateLoss();
    }

    private void showOneFragment() {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (oneFragment == null) {
            oneFragment = OverlapOneFragment.newInstance();
            transaction.add(R.id.fl_container, oneFragment);
        } else {
            transaction.show(oneFragment);
        }
        transaction.commitAllowingStateLoss();
    }

2. 问题分析及复现

考虑到APP正常使用时不会出现该问题,而长期置于后台后返回可能出现该问题,怀疑是因为APP长期处于后台,首页Activity被系统销毁,而返回APP时,重建首页Activity,Fragment也在此时被重建,引起Fragment显示错乱。
在隐藏和显示Fragment处添加打印验证问题:

    private void showOneFragment() {
        ...
        if (oneFragment == null) {
            Logger.e("oneFragment为null,新建");
            ...
        } else {
            transaction.show(oneFragment);
        }
    }

    private void hideAllFragment() {
        ...
        if (threeFragment != null) {
            transaction.hide(threeFragment);
        } else {
            Logger.e("threeFragment 为null,无需隐藏");
        }
        ...
    }

为便于验证该问题,打开“开发者选项”->“不保留活动”功能,模拟Activity被系统销毁的情形,运行程序,首页Activity销毁重建时的打印如下:
Activity销毁重建日志1
打印日志验证了我们的猜想,即因为APP长期处于后台,首页Activity被系统销毁,而返回APP时,重建首页Activity,此时默认显示第一个Fragment,这就是为什么退出时停留在第三个Fragment,而返回时显示第一个Fragment;
重建Activity时,Fragment也会被重建,并保留之前的视图状态(隐藏第一第二个Fragment,显示第三个Fragment),重建后变量threeFragment为null,并不会在调用hideAllFragment()方法时隐藏第三个Fragment,因此返回App时会显示第三个Fragment,又由于调用showThreeFragment()方法时会新建一个ThreeFragment并赋值给变量threeFragment,导致此时存在两个ThreeFragment实例,因此切换选项卡时,新建的ThreeFragment会隐藏,系统恢复的ThreeFragment不会隐藏,在视觉上产生ThreeFragment没有隐藏的现象。

3. 解决方法

既然系统会恢复Fragment,那么我们就尽可能的复用之前的Fragment,在Activity中有一个onAttachFragment(Fragment fragment)方法,会在一个Fragment附着到Activity上时调用,利用该特性,添加如下代码:

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        Logger.e("fragment is " + fragment.toString());
        if (oneFragment == null && fragment instanceof OverlapOneFragment) {
            Logger.e("onAttachFragment 赋值oneFragment");
            oneFragment = (OverlapOneFragment) fragment;
        } else if (twoFragment == null && fragment instanceof OverlapTwoFragment) {
            Logger.e("onAttachFragment 赋值twoFragment");
            twoFragment = (OverlapTwoFragment) fragment;
        } else if (threeFragment == null && fragment instanceof OverlapThreeFragment) {
            Logger.e("onAttachFragment 赋值threeFragment");
            threeFragment = (OverlapThreeFragment) fragment;
        }
    }

运行程序,打印日志如下:
Activity销毁重建日志2.png
此时不再出现Fragment重叠显示的问题,但用户离开时停留在第三个Fragment,现在返回时显示的是第一个Fragment,还需进一步优化。
利用Activity异常情况的生命周期,在onSaveInstanceState()方法中保存用户当前停留位置。
Activity异常情况生命周期
修改代码如下:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        int curTabPosition = tabLayout.getSelectedTabPosition();
        outState.putInt(CURRENT_TAB_POSITION, curTabPosition);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        if (savedInstanceState != null) {
            curPosition = savedInstanceState.getInt(CURRENT_TAB_POSITION, INDEX_ONE);
        }
        initView();
    }

    private void initView() {
        ...
        tabLayout.getTabAt(curPosition).select();
    }

经验证,这样修改代码后已解决Fragment重叠显示的问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值