防止fragment反复实例化

简书同步发布:http://www.jianshu.com/p/13fd3648cf9b

fragment很多优势,但也很多坑。
公司的项目切换fragment时都使用了replace(),我想这样会不会让fragment反复地实例化呢?能不能优化呢?于是开始了探索。

分析可能有点乱,先直接上结论:使用add()/show()可避免反复实例化。但是条件是不和addtobackstack()同时使用,并且重写onSaveInstanceState(),当然也不能反复new fragment()。

简单的代码示例

public class MainActivity extends BaseActivity{
    private HomeFragment homeFragment;
    private ChatFragment chatFragment;

    private HomeFragment getHomeFragment(){
        if(homeFragment == null){
            homeFragment = new HomeFragment();
        }
        return homeFragment;
    }

    private ChatFragment chatFragment(){
        if(chatFragment == null){
            chatFragment = new ChatFragment();
        }
        return chatFragment;
    }

    protected void onCreate(Bundle savedInstanceState) {
        ......
        //switchFragment()是自己写的方法,下文会有补充
        switchFragment(getHomeFragment());
        ......
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //这里不用调用super.onSaveInstanceState(outState);
        }
}
public class BaseActivity extends Activity{
        private Fragment currentFragment;

        protected void switchFragment(Fragment targetFragment){
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                //第一次使用switchFragment()时currentFragment为null,所以要判断一下
                if (currentFragment != null) {    
                     transaction.hide(currentFragment);
                }
                //如已添加过,则show就行了
                if (targetFragment.isAdded()) {
                    transaction.show(targetFragment);
                } else {
                    transaction.add(R.id.container, targetFragment);
                }
                transaction.commit();
                currentFragment = targetFragment;
        }
}

防止fragment反复实例化_第1张图片

首先是选择replace()还是add()/show()。

这是个基础知识,我们应优先使用add()/show()。因为使用replace(),被隐藏的fragment就会被销毁。 经过本人测试,如果fragment A被fragment B replace掉,那么A是会被销毁的,会走onDestroy() 。再切换回A(即使还是同一个对象),A会被重新创建并且触发onCreate()。所以replace是不行的。而add()/show()则不会被销毁和重新实例化,所以应该用add()/show()。

用add()/show()会遇上什么问题?

如果add()/show()正常工作,我们就省心多了。公司的APP的注册流程一共有三个步骤,我用了三个fragment界面,并用上add()/show()的同时使用了addtobackstack()方法。这时候就出现了诡异的现象,界面重叠、点返回没反应、show()下一界面没效果等等。本人一时搞不懂,也要赶项目没有深入探索,留坑待填,也希望有前辈能指点。
考虑到一般用户只会注册一次,频率很小,注册流程我就直接用replace()了。其他业务界面还是可以用add()/show()的。

如何避免不断地new fragment()

在不需要addtobackstack()的地方我还是可以用add()/show()替换replace()的。公司项目里不仅全用了replace(),还不断的通过new来创建fragment。

    ft.replace(R.id.container, new HomeFragment());

显然这里也有优化空间。

一开始我是用懒汉模式保证fragment是单例,但是这有一些弊端。

public class ExampleFragment extends Fragment{
      private static ExampleFragment exampleFragment;

      private ExampleFragment(){}

      public static ExampleFragment getExampleFragment(){
            if(exampleFragment == null){
                  exampleFragment = new ExampleFragment();
            }
            return exampleFragment;
      }
}


首先系统不能回收这个静态的引用,如果所有fragment类都使用了这种懒汉模式,那么可能占了很多内存不能被回收。另外还有一个更大的问题,就是有时这个fragment引用还在,但是它指向堆内存的实例对象被回收了,我们却不知情。这时候就会出现界面空白,因为这个fragment实例实际上被销毁了,但是却不会奔溃报NullPointer。网友指出解决办法可以是在onDetach()把静态引用设为null。这的确不会出现空白情况了,但是却违背了我们的初衷:避免不断地重新实例化fragment,因为我在实际使用中经常会出现空白的情况,这时把静态引用设为null,下次用到这个fragment时又会重新实例化。。。实在是哭笑不得。。。

于是我用另外一种方法,在activity中持有fragment 的实例。

public class MainActivity extends BaseActivity{
    private HomeFragment homeFragment;
    private HomeFragment getHomeFragment(){
        if(homeFragment == null){
            homeFragment = new HomeFragment();
        }
        return homeFragment;
    }

    protected void onCreate(Bundle savedInstanceState) {
        ......
        //switchFragment()是自己写的方法,下文会有补充
        switchFragment(getHomeFragment());
        ......
    }
}

用add()/show()的注意点

另外,网上也有朋友指出另一个坑:activity被意外销毁然后恢复时,会出现重叠问题。最简单的解决方法是重写onSaveInstanceState(),但是方法里面什么也不做。

public class MainActivity extends BaseActivity{
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //这里不用调用super.onSaveInstanceState(outState);
        }
}

此方案的局限

当我们重复打开某一个Activity时,fragment还是会重复实例化的。因为fragment实例是被activity持有的,如果那个activity被销毁或重新创建,那么fragment也会随之被销毁或重新创建。希望有朋友能提出更好的解决方法。

不过这种方案也有用武之地,如果贵公司的APP也是采用类似微信底部导航栏的界面,并且不能左滑/右滑到相邻的页面,那么用这种方案就很适合了。你想想,主界面使用频率这么高,每次按到一个分页都要重新实例化,重新加载数据,重新显示界面,那用户体验也挺感人的,对吧?

写在最后

要避免反复实例化fragment,就用add()/show()吧。在activity中持有fragment 的实例并避免反复new fragment,fragment一些天然的坑我们也要避开。

fragment还是有很多优势的。它启动速度比activity快多了,能提高用户体验。如果我们的APP要改需求,使用fragment改起来也是很轻松的。我以前的公司的项目是直接用activity显示内容的,主页是带侧滑页slidemenu的,然后要改成微信底部导航栏那种界面,改起来那酸爽。。。同样的改动在现在的公司又发生了,可是我们都是用fragment显示内容,改起来轻松很多。

add()/show()和addtobackstack()同时使用是不是一定有坑我也不确定,原因就更不清楚了,不过我的确是碰上坑了,而换成replace和addtobackstack搭配就没问题,在此抛砖引玉哈。最后,本人从事Android开发一年多,经验不足,这文章中如有谬误,多多指教哈~

你可能感兴趣的:(Android开发分享)