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的管理!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM