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

问题:

使用 AS 在 Fragment 中调用 getActivity() 方法的时候会出现可能为空指针的提醒

  1. 思考

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

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

    getActivity()不是在当前Fragment中调用的么,当前Fragment怎么会onDetach()呢?

  • 源码

  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. 以下源码基于 API 26
    1. getActivity 可能为 Null, 跟进源码,可以看到 getActivity() 是通过 mHost 成员变量获取 activity ,假如 mHost 为 null , 就返回一个 Null 。
        final public Activity getActivity() { return mHost == null ? null : mHost.getActivity(); }
    1. 那成员变量 mHost 是什么呢?什么时候赋值的?
      • mHost: 抽象类 android.app.FragmentHostCallback,继承之: android.app.FragmentContainer
      • mHost 传入,是在 Activity 初始化的时候传入的,源码如下:
    2. 当调用 getActivity() 的时候,Fragment 已经 onDetach() 从 Activity 中。所以会造成空指针。
    3. 如果app长时间在后台运行,再次进入app的时候可能会出现crash,Activity可能被系统回收然后重建,但是Fragment不会随着Activity的回收而被回收,创建的Fragment会被保存到Bundle里面,从而导致Fragment丢失对于的Activity.

            为了解决这个问题我想到一个解决办法就是当Activity销毁的时候绑定的Fragmetn同时销毁,在Activity里面重写onSaveInstanceState方法,并注释掉super.onSaveInstanceState(outState),让Activity不保存Fragment的状态.。

解决办法

  • 在 BaseFragment 的 onAttach() 方法中获取 Activity 并赋值给成员变量,然后在需要使用 getActivity() 的地方使用该成员变量就可以了。

记录:创建一个Fragment的过程

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

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

使用 AS 在 Fragment 中调用 getActivity() 方法的时候会出现可能为空指针的提醒

  1. 思考

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

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

    getActivity()不是在当前Fragment中调用的么,当前Fragment怎么会onDetach()呢?

  • 源码

  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. 以下源码基于 API 26
    1. getActivity 可能为 Null, 跟进源码,可以看到 getActivity() 是通过 mHost 成员变量获取 activity ,假如 mHost 为 null , 就返回一个 Null 。
        final public Activity getActivity() { return mHost == null ? null : mHost.getActivity(); }
    1. 那成员变量 mHost 是什么呢?什么时候赋值的?
      • mHost: 抽象类 android.app.FragmentHostCallback,继承之: android.app.FragmentContainer
      • mHost 传入,是在 Activity 初始化的时候传入的,源码如下:
    2. 当调用 getActivity() 的时候,Fragment 已经 onDetach() 从 Activity 中。所以会造成空指针。
    3. 如果app长时间在后台运行,再次进入app的时候可能会出现crash,Activity可能被系统回收然后重建,但是Fragment不会随着Activity的回收而被回收,创建的Fragment会被保存到Bundle里面,从而导致Fragment丢失对于的Activity.

            为了解决这个问题我想到一个解决办法就是当Activity销毁的时候绑定的Fragmetn同时销毁,在Activity里面重写onSaveInstanceState方法,并注释掉super.onSaveInstanceState(outState),让Activity不保存Fragment的状态.。

解决办法

  • 在 BaseFragment 的 onAttach() 方法中获取 Activity 并赋值给成员变量,然后在需要使用 getActivity() 的地方使用该成员变量就可以了。

记录:创建一个Fragment的过程

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

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

猜你喜欢

转载自www.cnblogs.com/tingtasia/p/11545014.html