Android-Fragment 中使用 getActivity()为null的原因---剖析源码

一、背景

问题

在使用fragment的时候经常会遇到getActivity()为null的情况。比如在一个异步网路请求的回调中调用了getActivity()就会出现空指针问题。之前解决这个问题,通常都是直接加空指针判断来规避,但是这并没有真正解决问题。

思考

为什么会出现这种情况,按说当前 Activity 存在,在 Fragment 中使用 getActivity() 是可以拿到的,不应该为空的。

很多人都曾被这个问题所困扰,如果app长时间在后台运行,再次进入app的时候可能会出现crash,而且fragment会有重叠现象。如果系统内存不足、切换横竖屏、app长时间在后台运行,Activity都可能会被系统回收然后重建,但Fragment并不会随着Activity的回收而被回收,创建的所有Fragment会被保存到Bundle里面,从而导致Fragment丢失对应的Activity。

如果从最近使用的应用里面点击我们的应用,系统会恢复之前被回收的Activity,这个时候Activity在oncreate里面也会做Fragment的恢复,

原因:你在调用了getActivity()时,当前的Fragment已经onDetach()了宿主Activity。比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。

二、源码

1、fragment 的生命周期

官方 Fragment 生命周期图

2、可以看到Fragment比Activity多了几个额外的生命周期回调方法

onAttach:

onAttach()在fragment与Activity关联之后调调查用。需要注意的是,初始化fragment参数可以从getArguments()获得,但是,当Fragment附加到Activity之后,就无法再调用setArguments()。所以除了在最开始时,其它时间都无法向初始化参数添加内容。

onCreate:

fragment初次创建时调用。尽管它看起来像是Activity的OnCreate()函数,但这个只是用来创建Fragment的。此时的Activity还没有创建完成,因为我们的Fragment也是Activity创建的一部分。所以如果你想在这里使用Activity中的一些资源,将会获取不到。比如:获取同一个Activity中其它Frament的控件实例。(代码如下:),如果想要获得Activity相关联的资源,必须在onActivityCreated中获取。

onCreateView:

在这个fragment构造它的用户接口视图(即布局)时调用。

onActivityCreated:

在Activity的OnCreate()结束后,会调用此方法。所以到这里的时候,Activity已经创建完成!在这个函数中才可以使用Activity的所有资源。如果把下面的代码放在这里,获取到的btn_Try的值将不会再是空的!

onStart:

当到OnStart()时,Fragment对用户就是可见的了。但用户还未开始与Fragment交互。在生命周期中也可以看到Fragment的OnStart()过程与Activity的OnStart()过程是绑定的。意义即是一样的。以前你写在Activity的OnStart()中来处理的代码,用Fragment来实现时,依然可以放在OnStart()中来处理。

onResume:

当这个fragment对用户可见并且正在运行时调用。这是Fragment与用户交互之前的最后一个回调。从生命周期对比中,可以看到,Fragment的OnResume与Activity的OnResume是相互绑定的,意义是一样的。它依赖于包含它的activity的Activity.onResume。当OnResume()结束后,就可以正式与用户交互了。

onPause:

此回调与Activity的OnPause()相绑定,与Activity的OnPause()意义一样。

onStop:

这个回调与Activity的OnStop()相绑定,意义一样。已停止的Fragment可以直接返回到OnStart()回调,然后调用OnResume()。

onDestroyView:

如果Fragment即将被结束或保存,那么撤销方向上的下一个回调将是onDestoryView()。会将在onCreateView创建的视图与这个fragment分离。下次这个fragment若要显示,那么将会创建新视图。这会在onStop之后和onDestroy之前调用。这个方法的调用同onCreateView是否返回非null视图无关。它会潜在的在这个视图状态被保存之后以及它被它的父视图回收之前调用。

onDestroy:

当这个fragment不再使用时调用。需要注意的是,它即使经过了onDestroy()阶段,但仍然能从Activity中找到,因为它还没有Detach。

onDetach:

Fragment生命周期中最后一个回调是onDetach()。调用它以后,Fragment就不再与Activity相绑定,它也不再拥有视图层次结构,它的所有资源都将被释放。

3、所以到底发生了什么?

假设我们的页面叫MyActivity,其中用到的Fragment叫MyFragment。

出现上面这种情况时,app发生的变化如下:

1、在前面提到的几种情况下系统回收了MyActivity

2、通过onSaveInstanceState保存MyFragment的状态

3、用户再次点击进入app

4、由于MyActivity被回收,系统会重启MyActivity,根据之前保存的MyFragment的状态恢复fragment

5、MyActivity的代码逻辑中,会再次创建新的MyFragment

6、页面出现混乱,覆盖了两层的fragment。假如恢复的MyFragment使用到了getActivity()方法,会报空指针异常

4、对于上面的问题,可以考虑下面这两种解决办法:

1、不保存fragment的状态

在MyActivity中重写onSaveInstanceState方法,将super.onSaveInstanceState(outState);注释掉,让其不再保存Fragment的状态,达到fragment随MyActivity一起销毁的目的。

2、重建时清除已经保存的fragment的状态

在恢复Fragment之前把Bundle里面的fragment状态数据给清除。方法如下:

if(savedInstanceState!= null)
{
    String FRAGMENTS_TAG =  "android:support:fragments";
    savedInstanceState.remove(FRAGMENTS_TAG);
}

三、如何创建一个Fragment的过程

动态添加Fragment主要分为4步:
1.获取到FragmentManager,在V4包中通过getSupportFragmentManager,在系统中原生的Fragment是通过getFragmentManager获得的。
2.开启一个事务,通过调用beginTransaction方法开启。
3.向容器内加入Fragment,一般使用add或者replace方法实现,需要传入容器的id和Fragment的实例。
4.提交事务,调用commit方法提交。
这部分有关fragment的操作看不大懂也没关系,下节我们会具体讲有关Fragment的管理!

FragmentManager manager = getSupportFragmentManager();  
FragmentTransaction transaction = manager.beginTransaction();  
Fragment1 fragment1 = new Fragment1();  
transaction.add(R.id.fragment_container, fragment1);  
transaction.commit();  

Java基础不好的小水怪,正在学习。有错请指出,一起加油。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值