Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 467|回复: 0

Android中管理多个Fragment的最佳实践,完美解决保存状态与重影问题

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-12 16:09:52 | 显示全部楼层 |阅读模式

    自从在Android 3.0引入Fragment以来,它被使用的频率也随之增多。Fragment带来的好处不言而喻,解决了不同屏幕分辨率的动态和灵活UI设计。但是在Activity管理多个Fragment中,通常会遇到这些问题:

    1、Fragment的状态保存

    2、Fragment的重影

    当然,这些问题也一直出现我的开发过程中,虽然有时候通过各种手段也能解决一些问题,但是总是同时完美解决这两个问题。近来因为项目需要,查阅了很多官方资料(Android官方资料也慢慢有中文资料了,我大Google果然是Don't be evil,扯远了~~),终于彻底解决了这些问题。

    设备:nexus 5

    条件:

    1、打开“不保留活动”(开发者选项里,主要用于模拟Activity被及时回收)

    2、关闭“不保留活动”(正常状态下)

    结果:目前没发现问题,由于设备有限,大家如果发现在其他设备上有问题,请在下方回帖!

     

    首先我先来解释下上面问题出现的原因:

    1、有时候,我们需要在多个Fragment间切换,并且保存每个Fragment的状态。官方的方法是使用replace()来替换Fragment,但是replace()的调用会导致Fragment的onCreteView()被调用,所以切换界面时会无法保存当前的状态。因此一般采用add()、hide()与show()配合,来达到保存Fragment的状态。以下为代码片段:

    private void setTabSelection(int position) {
            //记录position
            this.position = position;
            //更改底部导航栏按钮状态
            changeButtonStatus(position);
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            // 先隐藏掉所有的Fragment,以防止有多个Fragment显示在界面上的情况
            hideFragments(transaction);
            switch (position) {
                case TAB_HOME:
                    btnHomePager.setSelected(true);
                    btnShoppingCart.setSelected(false);
                    btnMine.setSelected(false);
                    if (homeFragment == null) {
                        homeFragment = new HomePagerFragment();
                        transaction.add(R.id.fragment_container, homeFragment);
                    } else {
                        transaction.show(homeFragment);
                    }
                    break;
                case TAB_SHOP:
                    btnHomePager.setSelected(false);
                    btnShoppingCart.setSelected(true);
                    btnMine.setSelected(false);
                    if (shoppingFragment == null) {
                        shoppingFragment = new ShoppingCartFragment();
                        transaction.add(R.id.fragment_container, shoppingFragment);
                    } else {
                        transaction.show(shoppingFragment);
                    }
                    break;
                case TAB_MINE:
                    btnHomePager.setSelected(false);
                    btnShoppingCart.setSelected(false);
                    btnMine.setSelected(true);
                    if (mineFragment == null) {
                        mineFragment = new MineFragment();
                        transaction.add(R.id.fragment_container, mineFragment);
                    } else {
                        transaction.show(mineFragment);
                    }
                    break;
            }
            transaction.commitAllowingStateLoss();
        }

     

    2、第二个问题的出现正是因为使用了Fragment的状态保存,当系统内存不足,Fragment的宿主Activity回收的时候,Fragment的实例并没有随之被回收。Activity被系统回收时,会主动调用onSaveInstance()方法来保存视图层(View Hierarchy),所以当Activity通过导航再次被重建时,之前被实例化过的Fragment依然会出现在Activity中,然而从上述代码中可以明显看出,再次重建了新的Fragment,综上这些因素导致了多个Fragment重叠在一起。

     

    我尝试了很多种方法去解决这个问题,比如:

    在onSaveInstance()里面去remove()所有非空的Fragment,然后在onRestoreInstanceState()中去再次按照问题一的方式创建Activity。当我处于打开“不保留活动”的时候,效果非常令人满意,然而当我关闭“不保留活动”的时候,问题却出现了。当转跳到其他Activity、打开多任务窗口、使用Home回到主屏幕再返回时,发现根本没有Fragment了,一篇空白。

    于是跟踪下去,我调查了onSaveInstanceState()与onRestoreInstanceState()这两个方法。原本以为只有在系统因为内存回收Activity时才会调用的onSaveInstanceState(),居然在转跳到其他Activity、打开多任务窗口、使用Home回到主屏幕这些操作中也被调用,然而onRestoreInstanceState()并没有在再次回到Activity时被调用。而且我在onResume()发现之前的Fragment只是被移除,并不是空,所以就算你在onResume()中执行问题一中创建的Fragment的方法,同样无济于事。所以通过remove()宣告失败。

    接着通过调查资料发现Activity中的onSaveInstanceState()里面有一句super.onRestoreInstanceState(savedInstanceState),Google对于这句话的解释是“Always call the superclass so it can save the view hierarchy state”,大概意思是“总是执行这句代码来调用父类去保存视图层的状态”。其实到这里大家也就明白了,就是因为这句话导致了重影的出现,于是我删除了这句话,然后onCreate()与onRestoreInstanceState()中同时使用问题一中的创建Fragment方法,然后再通过保存切换的状态,发现结果非常完美。代码如下:

        //记录Fragment的位置
        private int position = 0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_index);
    
            setTabSelection(position);
        }
    
        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
            position = savedInstanceState.getInt("position");
            setTabSelection(position);
            super.onRestoreInstanceState(savedInstanceState);
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            //记录当前的position
            outState.putInt("position", position);
        }

     

    记录于此,希望能帮助到一些正遇到这种问题的朋友!

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-16 10:53 , Processed in 0.066909 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表