怎样用好Fragment,避免掉坑(一)

原创 2016年06月01日 21:19:04

本文是对Fragment一些坑的处理做一个记录,文章是参考YoKey 简书的Fragment全解析系列,想看更详细的可以去看,

我这里只是 简要总结一下,提炼精华。


1.内存重启

安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate方法调用后紧接着恢复(从onAttach生命周期开始)


FragmentManager无论何时都会负责把Fragment保存起来


2.getActivity()为null

出现原因:这种情况一般出现在内存重启了,因为fragment的生命周期是和activity绑定的,执行了onDetach后fragment已经和activity断开联系。可能会造成这种异常,

解决方式

在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是相比空指针闪退,这种做法“安全”些),即:

protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    this.mActivity = activity;
}

/**
*  如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替
*/
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    this.mActivity = (Activity)context;
}

3.Fragment重叠异常

出现原因如果你add()了几个Fragment,使用show()、hide()方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。原因是FragmentManager帮我们管理Fragment,每当我们离开该Activity,FragmentManager都会保存它的Fragments,当发生“内存重启”,他会从栈底向栈顶的顺序一次性恢复Fragments,并且全都都是以show()的方式,所以我们看到了界面重叠。(如果是replace,恢复形式和Activity一致,只有当你pop之后上一个Fragment才开始重新恢复,所有使用replace不会造成重叠现象)

解决方式

(1)使用findFragmentByTag

即在add()或者replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag找到对应的Fragment,并hide()需要隐藏的fragment。

写法如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);

    TargetFragment targetFragment;
    HideFragment hideFragment;

    if (savedInstanceState != null) {  // “内存重启”时调用
        targetFragment = getSupportFragmentManager().findFragmentByTag(targetFragment.getClass().getName);
        hideFragment = getSupportFragmentManager().findFragmentByTag(hideFragment.getClass().getName);
        // 解决重叠问题
        getFragmentManager().beginTransaction()
                .show(targetFragment)
                .hide(hideFragment)
                .commit();
    }else{  // 正常时
        targetFragment = TargetFragment.newInstance();
        hideFragment = HideFragment.newInstance();

        getFragmentManager().beginTransaction()
                .add(R.id.container, targetFragment, targetFragment.getClass().getName())
                .add(R.id,container,hideFragment,hideFragment.getClass().getName())
                .hide(hideFragment)
                .commit();
    }
}
如果你想恢复到用户离开时的那个Fragment的界面,你还需要在onSaveInstanceState(Bundle outState)里保存离开时的那个可见的tag或下标,在onCreate“内存重启”代码块中,取出tag/下标,进行恢复。


(2)用getSupportFragmentManager().getFragments()恢复

通过getFragments()可以获取到当前FragmentManager管理的栈内所有Fragment。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);

    TargetFragment targetFragment;
    HideFragment hideFragment;

    if (savedInstanceState != null) {  // “内存重启”时调用
        List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
        for (Fragment fragment : fragmentList) {
            if(fragment instanceof TartgetFragment){
               targetFragment = (TargetFragment)fragment; 
            }else if(fragment instanceof HideFragment){
               hideFragment = (HideFragment)fragment;
            }
        }
        // 解决重叠问题
        getFragmentManager().beginTransaction()
                .show(targetFragment)
                .hide(hideFragment)
                .commit();
    }else{  // 正常时
        targetFragment = TargetFragment.newInstance();
        hideFragment = HideFragment.newInstance();

        // 这里add时,tag可传可不传
        getFragmentManager().beginTransaction()
                .add(R.id.container)
                .add(R.id,container,hideFragment)
                .hide(hideFragment)
                .commit();
    }
}
从代码看起来,这种方式比较复杂,但是这种方式在一些场景下比第一种方式更加简便有效。


注意:有的朋友会在 onSaveInstanceState回调时去保存当前的fragment,重启时又把fragment拿出来,例如这样写

// 保存
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    getSupportFragmentManager().putFragment(outState, KEY, targetFragment);
}
// 恢复
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_scrolling);

    if (savedInstanceState != null) {
        Fragment targetFragment = getSupportFragmentManager().getFragment(savedInstanceState, KEY);
    }
}
仅仅为了找回栈内的Fragment,使用putFragment(bundle, key, fragment)保存fragment,是完全没有必要的;因为FragmentManager在任何情况都会帮你存储Fragment,你要做的仅仅是在“内存重启”后,找回这些Fragment即可。

















版权声明:本文为博主原创文章,未经博主允许不得转载。

Fragment快速切换导致闪退的解决方法

频繁调用事务 getFragmentManager().beginTransaction().replace(R.id.cont, woDeFragment).commit(); 或者add方...
  • zhan10001
  • zhan10001
  • 2017年01月17日 18:18
  • 676

Fragment涉及的add、remove和replace方法和回退栈的关系详解

在现在的Android开发中,Fragment属于比较常用的了,在使用Fragment过程中,add、remove和replace这三个方法是比较常用的,也是容易引发歧异的,再加入回退栈的功能就更乱了...
  • haozipi
  • haozipi
  • 2015年07月22日 00:10
  • 5703

Android 实现同个Activity中存在多个Fragment多次切换之后依次返回(一)(Fragment回退栈简单使用)

本篇博文记录自己所用到的关于Fragment回退栈的使用 什么情况会使用Fragment的回退栈 什么情况会用到Fragment的回退栈,假设现在要在一个Activity上不停的切换Frag...
  • xuyonghong1122
  • xuyonghong1122
  • 2016年03月15日 16:26
  • 4632

Activity管理Fragment之Fragment关闭当前回到之前Fragment

直接上代码说明: 首先默认你已经写好了一个Activity并至少添加了一个Fragment,下面是我的Activity中Fragment的布局代码和java代码; Activity中Fragment的...
  • u012382791
  • u012382791
  • 2016年03月31日 17:32
  • 8034

该怎样用好CorelDRAW中的“轮廓笔工具”

在绘图过程中,通过修改对象的轮廓属性,可以起到修饰对象的作用
  • coreldraw2016
  • coreldraw2016
  • 2016年06月08日 14:55
  • 1407

怎样用好Google进行搜索

对于任何一个人,学习使用Google都将对你的工作学习有很大帮助。虽然Google已经做的非常的简单,只要会打字的人都能使用起来,但是如何用好却不是那么简单。     使用搜索引擎之前,一定需要...
  • wangyi1988wang
  • wangyi1988wang
  • 2014年05月18日 21:49
  • 482

MindManager中的时间轴模板该怎样用好

新发布的MindManager 2017,除了对流程图做了改进及增强外,还新增了一种时间轴导图模板。利用新增的时间轴布局功能,用户能够清楚地说明产品路线图、制作项目里程碑和营销发布计划等。   Mi...
  • siweidaotu
  • siweidaotu
  • 2016年12月08日 11:47
  • 2566

怎样用好Google进行搜索

对于任何一个人,学习使用Google都将对你的工作学习有很大帮助。虽然Google已经做的非常的简单,只要会打字的人都能使用起来,但是如何用好却不是那么简单。     使用搜索引擎之前,一定需要先自...
  • mociml
  • mociml
  • 2012年04月17日 08:56
  • 751

怎样用好Google进行搜索

对于任何一个人,学习使用Google都将对你的工作学习有很大帮助。虽然Google已经做的非常的简单,只要会打字的人都能使用起来,但是如何用好却不是那么简单。     使用搜索引擎之前,一定需要先自...
  • WSKinght
  • WSKinght
  • 2014年05月18日 23:06
  • 735

你该怎样用svn才能避免冲突? (内附逻辑图和详细解释)

我之前在某公司实习, 做手机应用kaif
  • stpeace
  • stpeace
  • 2014年05月08日 00:19
  • 3671
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:怎样用好Fragment,避免掉坑(一)
举报原因:
原因补充:

(最多只允许输入30个字)