淺談 Fragment 生命周期

版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
微博:厲圣杰
源碼:AndroidDemo/Fragment
文中如有紕漏,歡迎大家留言指出。

Fragment 是在 Android 3.0 中引入,用于解決不同屏幕分辨率的設(shè)備上 UI 顯示、交互的問(wèn)題。Fragment 有自己的布局,有自己的生命周期,有自己的事件響應(yīng)。

但 Fragment 又是依賴(lài)于 Activity 存在的,你可以把多個(gè) Fragment 嵌入到一個(gè) Activity 中或者多個(gè) Activity 重用一個(gè) Fragment。Activity 的生命周期直接影響 Fragment 的生命周期。所以要正確的使用 Fragment,首先必要從根本上了解 Fragment 的生命周期,俗話(huà)說(shuō):工欲善其事必先利其器

簡(jiǎn)單來(lái)說(shuō),F(xiàn)ragment 的生命周期可以用下圖來(lái)表示:


fragment_lifecycle

是不是覺(jué)得這圖看著很眼熟,沒(méi)錯(cuò),前面講過(guò) Fragment 是依賴(lài)于 Activity 存在的,所以 Fragment 的生命周期跟 Activity 的生命周期很相似。

下圖很好的描述了 Fragment 與 Activity 生命周期的關(guān)系,請(qǐng)看圖~


fragment_and_activity_lifecycle

Fragment 的生命周期這樣就講完了?理論上來(lái)說(shuō),是的。我說(shuō)樓主,你還真是淺談別急,下面還有呢

上面只是展示了 Fragment 與 Activity 生命周期最基本的關(guān)系,如果通過(guò) addToBackStack() 將 Fragment 放入回退棧,然后通過(guò) popBackStack() 出棧,F(xiàn)ragment 的生命周期會(huì)如何變化呢?如果 Fragment 與 ViewPager 結(jié)合使用,F(xiàn)ragment 的生命周期又是如何?如果通過(guò) hide()show() 方法來(lái)展示隱藏,這時(shí) Fragment 的生命周期又會(huì)如何?

![屏幕快照 2017-01-04 下午8.30.54](http://odsdowehg.bkt.clouddn.com/屏幕快照 2017-01-04 下午8.30.54.png)

不急,先看思維導(dǎo)圖中的問(wèn)題,然后咱們就來(lái)研究一下上訴問(wèn)題~

既然是 Fragment 的生命周期,那自然是少不了對(duì) Fragment 生命周期的監(jiān)測(cè),怎么辦?打 Log。

項(xiàng)目結(jié)構(gòu)如下
![屏幕快照 2017-01-04 下午8.43.37](http://odsdowehg.bkt.clouddn.com/屏幕快照 2017-01-04 下午8.43.37.png)

本文涉及到的類(lèi)主要集中在 lifecircle 包中及 LifeCircleFragment。代碼已上傳 AndroidDemo/Fragment ,這里貼出 LifeCircleFragment 的主要代碼。

LifeCircleFragment.java

/**
 * 測(cè)試 Fragment 生命周期,setUserVisibleHint 初始進(jìn)來(lái)時(shí)只有默認(rèn) Tab
 * Created by littlejie on 2016/12/30.
 */

public class LifeCircleFragment extends BaseFragment {

    private final String TAG = LifeCircleFragment.class.getSimpleName();
    //截取 Fragment.toString() 方法中的標(biāo)識(shí)數(shù)字
    private final String ID = this.toString().substring(this.toString().indexOf("{") + 1, this.toString().length() - 1);
    private TextView mTvContent;

    //默認(rèn) Title 值
    private String mTitle = "Tab";

    public static LifeCircleFragment newInstance(String title) {
        Bundle args = new Bundle();

        LifeCircleFragment fragment = new LifeCircleFragment();
        args.putString(Constant.PARAM_TITLE, title);
        fragment.setArguments(args);
        return fragment;
    }

    /**
     * 當(dāng) Fragment 調(diào)用 hide() 、 show() 時(shí)回調(diào)
     * @param hidden
     */
    @Override
    public void onHiddenChanged(boolean hidden) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onHiddenChanged.hidden = " + hidden);
        super.onHiddenChanged(hidden);
    }

    /**
     * 當(dāng) Fragment 與 ViewPager 結(jié)合使用時(shí),切換 Pager 時(shí)回調(diào)
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is setUserVisibleHint.isVisibleToUser = " + isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
    }

    /**
     * Fragment 關(guān)聯(lián)到 Activity 時(shí)回調(diào)
     * 此時(shí) Activity 已經(jīng)與 Fragment 關(guān)聯(lián),通過(guò) Context 向下轉(zhuǎn)型,就可以與 Activity 通信
     * 當(dāng)然也可以使用 getActivity(),前提是這個(gè) fragment 已經(jīng)和宿主的 activity 關(guān)聯(lián),并且沒(méi)有脫離
     * onAttach 只調(diào)用一次。
     *
     * @param context
     */
    @Override
    public void onAttach(Context context) {
        //由于 onCreate 是在 onAttach 后執(zhí)行,故此時(shí) mTitle 為空
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onAttach.");
        super.onAttach(context);
    }

    /**
     * 系統(tǒng)創(chuàng)建 Fragment 的時(shí)候回調(diào),介于 onAttach() 和 onCreateView() 之間
     * 一般用于初始化一些數(shù)據(jù)
     * 值得注意的是,此時(shí) Activity 還在創(chuàng)建中,因此不能在執(zhí)行一些跟 Activity UI 相關(guān)的操作
     * 否則,會(huì)出現(xiàn)一些難以預(yù)料的問(wèn)題,比如:NullPointException
     * 如果要對(duì) Activity 上的 UI 進(jìn)行操作,建議在 onActivityCreated() 中操作
     *
     * @param savedInstanceState
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onCreate.");
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        if (getArguments() != null) {
            mTitle = getArguments().getString(Constant.PARAM_TITLE);
        }
        //測(cè)試 onCreate() 是 Activity 的 UI 是否初始化完成
        //if (getContext() instanceof LifeCircleActivity) {
        //((LifeCircleActivity) getContext()).setActivityCreated("Fragment 進(jìn)行 onCreate() 時(shí) Activity UI 尚未初始化完成。"
        //        + "\n你看不到我,因?yàn)槲乙呀?jīng)變成異常拋出了。");
        //}
    }

    /**
     * 創(chuàng)建 Fragment 需要顯示的 View,默認(rèn)返回 null。
     * 當(dāng)返回的 View 不為 null 時(shí),View 被銷(xiāo)毀時(shí)會(huì)調(diào)用 onDestroyView()
     * 此處應(yīng)該只進(jìn)行布局的初始化,而不應(yīng)該執(zhí)行耗時(shí)操作,如網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)讀取
     *
     * @param inflater
     * @param container
     * @param savedInstanceState
     * @return
     */
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onCreateView.");
        return inflater.inflate(R.layout.fragment_life_circle, container, false);
    }

    /**
     * 該方法在 onCreateView() 之后會(huì)被立即執(zhí)行
     * 此時(shí)可以對(duì) View 對(duì)象進(jìn)行賦值,當(dāng)然在 onCreateView() 中也可以執(zhí)行
     *
     * @param view
     * @param savedInstanceState
     */
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onViewCreated.");
        super.onViewCreated(view, savedInstanceState);
        mTvContent = (TextView) view.findViewById(R.id.tv_content);
        mTvContent.setText(mTitle);
    }

    /**
     * 當(dāng) Activity 執(zhí)行完 onCreate() 方法后調(diào)用
     * 此時(shí)可以執(zhí)行與 Activity 相關(guān)的 UI 操作
     *
     * @param savedInstanceState
     */
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onActivityCreated.");
        super.onActivityCreated(savedInstanceState);
        //測(cè)試 onCreate() 是 Activity 的 UI 是否初始化完成
        if (getContext() instanceof SimpleLifeCircleActivity) {
            ((SimpleLifeCircleActivity) getContext()).setActivityCreated("Fragment 進(jìn)行 onActivityCreated() 時(shí) Activity UI 已初始化完成。"
                    + "\n你能看到我。");
        }
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onViewStateRestored.");
        super.onViewStateRestored(savedInstanceState);
    }

    /*-----------跟 Activity 中對(duì)應(yīng)方法類(lèi)似-------------*/
    @Override
    public void onStart() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onStart.");
        super.onStart();
    }

    @Override
    public void onResume() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onResume.");
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onPause.");
        super.onPause();
    }

    @Override
    public void onStop() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onStop.");
        super.onStop();
    }
    /*-----------跟 Activity 中對(duì)應(yīng)方法類(lèi)似-------------*/

    /**
     * 表示銷(xiāo)毀 Fragment 相關(guān)聯(lián)的 UI 布局,清除所有跟視圖相關(guān)的資源。
     * 不一定在 Activity 的 onDestroy() 方法中調(diào)用
     * 如:Fragment 與 ViewPager 結(jié)合使用時(shí)
     *
     * @see com.littlejie.fragment.lifecircle.LifeCircleWithViewPagerActivity
     */
    @Override
    public void onDestroyView() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDestroyView.");
        super.onDestroyView();
    }

    /**
     * 銷(xiāo)毀 Fragment 對(duì)象的時(shí)候調(diào)用,一般是調(diào)用 Activity 的 onDestroy() 的時(shí)候執(zhí)行
     */
    @Override
    public void onDestroy() {
        //當(dāng)調(diào)用 Activity 的 onDestroy() 時(shí)調(diào)用
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDestroy.");
        super.onDestroy();
    }

    /**
     * 解除 Fragment 與 Activity 的關(guān)聯(lián)
     */
    @Override
    public void onDetach() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDetach.");
        super.onDetach();
    }

}

由于日志過(guò)多,這里不再展示,有興趣的可自行下載 Demo 測(cè)試。這里簡(jiǎn)要總結(jié)下關(guān)于 Fragment 生命周期。

  1. 第一點(diǎn)很重要,最開(kāi)始的兩張圖展示的 Fragment 與 Activity 的生命周期關(guān)系沒(méi)毛病。
  2. onAttach()onCreate() 只在 Fragment 與 Activity 第一次關(guān)聯(lián)時(shí)調(diào)用。
  3. onDestroy()onDetach() 只在 Fragment 的宿主 Activity 銷(xiāo)毀時(shí)才會(huì)被調(diào)用。
  4. 根據(jù)前 3 點(diǎn),將 Fragment 通過(guò) addToBackStack() 只涉及 onCreateView()onDestroyView() 這之間的生命周期。add()replace() 不會(huì)對(duì) Fragment 的生命周期產(chǎn)生影響,但 add() 方法會(huì)造成 Fragment 疊加顯示。
  5. Fragment 與 ViewPager 結(jié)合使用時(shí)的生命周期與第 4 點(diǎn)相似。
  6. 通過(guò) hide()show() 來(lái)隱藏、顯示Fragment,此時(shí) Fragment 只改變了可見(jiàn)性,并不涉及生命周期的改變
  7. 第 7 點(diǎn)與Fragment 和 Activity 的生命周期有關(guān),即:不要在 Fragment 的 onCreate() 方法中操作宿主Activity 的 UI。因?yàn)槟銦o(wú)法保證此時(shí) 宿主Activity 的 UI 已經(jīng)完全初始化。PS:某些情況下是可以確保 宿主Activity 已經(jīng)初始化完成的。

恩,的確算淺談,給出了自認(rèn)為正確的結(jié)論,歡迎各位前來(lái)打臉~

源碼地址:AndroidDemo/Fragment。歡迎大家 Star 和 Fork。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容