版權聲明:本文為博主原創文章,未經博主允許不得轉載。
微博:厲聖傑
源碼:AndroidDemo/Fragment
文中如有紕漏,歡迎大家留言指出。
Fragment 是在 Android 3.0 中引入,用於解決不同屏幕分辨率的設備上 UI 顯示、交互的問題。Fragment 有自己的布局,有自己的生命周期,有自己的事件響應。
但 Fragment 又是依賴於 Activity 存在的,你可以把多個 Fragment 嵌入到一個 Activity 中或者多個 Activity 重用一個 Fragment。Activity 的生命周期直接影響 Fragment 的生命周期。所以要正確的使用 Fragment,首先必要從根本上了解 Fragment 的生命周期,俗話說:工欲善其事必先利其器
簡單來說,Fragment 的生命周期可以用下圖來表示:
是不是覺得這圖看着很眼熟,沒錯,前面講過 Fragment 是依賴於 Activity 存在的,所以 Fragment 的生命周期跟 Activity 的生命周期很相似。
下圖很好的描述了 Fragment 與 Activity 生命周期的關系,請看圖~
Fragment 的生命周期這樣就講完了?理論上來說,是的。我說樓主,你還真是淺談別急,下面還有呢
上面只是展示了 Fragment 與 Activity 生命周期最基本的關系,如果通過 addToBackStack()
將 Fragment 放入回退棧,然后通過 popBackStack()
出棧,Fragment 的生命周期會如何變化呢?如果 Fragment 與 ViewPager 結合使用,Fragment 的生命周期又是如何?如果通過 hide()
和 show()
方法來展示隱藏,這時 Fragment 的生命周期又會如何?

不急,先看思維導圖中的問題,然后咱們就來研究一下上訴問題~
既然是 Fragment 的生命周期,那自然是少不了對 Fragment 生命周期的監測,怎么辦?打 Log。
項目結構如下

本文涉及到的類主要集中在 lifecircle
包中及 LifeCircleFragment。代碼已上傳 AndroidDemo/Fragment ,這里貼出 LifeCircleFragment
的主要代碼。
LifeCircleFragment.java
/**
* 測試 Fragment 生命周期,setUserVisibleHint 初始進來時只有默認 Tab
* Created by littlejie on 2016/12/30.
*/
public class LifeCircleFragment extends BaseFragment {
private final String TAG = LifeCircleFragment.class.getSimpleName();
//截取 Fragment.toString() 方法中的標識數字
private final String ID = this.toString().substring(this.toString().indexOf("{") + 1, this.toString().length() - 1);
private TextView mTvContent;
//默認 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;
}
/**
* 當 Fragment 調用 hide() 、 show() 時回調
* @param hidden
*/
@Override
public void onHiddenChanged(boolean hidden) {
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onHiddenChanged.hidden = " + hidden);
super.onHiddenChanged(hidden);
}
/**
* 當 Fragment 與 ViewPager 結合使用時,切換 Pager 時回調
* @param isVisibleToUser
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is setUserVisibleHint.isVisibleToUser = " + isVisibleToUser);
super.setUserVisibleHint(isVisibleToUser);
}
/**
* Fragment 關聯到 Activity 時回調
* 此時 Activity 已經與 Fragment 關聯,通過 Context 向下轉型,就可以與 Activity 通信
* 當然也可以使用 getActivity(),前提是這個 fragment 已經和宿主的 activity 關聯,並且沒有脫離
* onAttach 只調用一次。
*
* @param context
*/
@Override
public void onAttach(Context context) {
//由於 onCreate 是在 onAttach 后執行,故此時 mTitle 為空
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onAttach.");
super.onAttach(context);
}
/**
* 系統創建 Fragment 的時候回調,介於 onAttach() 和 onCreateView() 之間
* 一般用於初始化一些數據
* 值得注意的是,此時 Activity 還在創建中,因此不能在執行一些跟 Activity UI 相關的操作
* 否則,會出現一些難以預料的問題,比如:NullPointException
* 如果要對 Activity 上的 UI 進行操作,建議在 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);
}
//測試 onCreate() 是 Activity 的 UI 是否初始化完成
//if (getContext() instanceof LifeCircleActivity) {
//((LifeCircleActivity) getContext()).setActivityCreated("Fragment 進行 onCreate() 時 Activity UI 尚未初始化完成。"
// + "\n你看不到我,因為我已經變成異常拋出了。");
//}
}
/**
* 創建 Fragment 需要顯示的 View,默認返回 null。
* 當返回的 View 不為 null 時,View 被銷毀時會調用 onDestroyView()
* 此處應該只進行布局的初始化,而不應該執行耗時操作,如網絡請求、數據庫讀取
*
* @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() 之后會被立即執行
* 此時可以對 View 對象進行賦值,當然在 onCreateView() 中也可以執行
*
* @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);
}
/**
* 當 Activity 執行完 onCreate() 方法后調用
* 此時可以執行與 Activity 相關的 UI 操作
*
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onActivityCreated.");
super.onActivityCreated(savedInstanceState);
//測試 onCreate() 是 Activity 的 UI 是否初始化完成
if (getContext() instanceof SimpleLifeCircleActivity) {
((SimpleLifeCircleActivity) getContext()).setActivityCreated("Fragment 進行 onActivityCreated() 時 Activity UI 已初始化完成。"
+ "\n你能看到我。");
}
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onViewStateRestored.");
super.onViewStateRestored(savedInstanceState);
}
/*-----------跟 Activity 中對應方法類似-------------*/
@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 中對應方法類似-------------*/
/**
* 表示銷毀 Fragment 相關聯的 UI 布局,清除所有跟視圖相關的資源。
* 不一定在 Activity 的 onDestroy() 方法中調用
* 如:Fragment 與 ViewPager 結合使用時
*
* @see com.littlejie.fragment.lifecircle.LifeCircleWithViewPagerActivity
*/
@Override
public void onDestroyView() {
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDestroyView.");
super.onDestroyView();
}
/**
* 銷毀 Fragment 對象的時候調用,一般是調用 Activity 的 onDestroy() 的時候執行
*/
@Override
public void onDestroy() {
//當調用 Activity 的 onDestroy() 時調用
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDestroy.");
super.onDestroy();
}
/**
* 解除 Fragment 與 Activity 的關聯
*/
@Override
public void onDetach() {
Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDetach.");
super.onDetach();
}
}
由於日志過多,這里不再展示,有興趣的可自行下載 Demo 測試。這里簡要總結下關於 Fragment 生命周期。
- 第一點很重要,最開始的兩張圖展示的 Fragment 與 Activity 的生命周期關系沒毛病。
onAttach()
和onCreate()
只在 Fragment 與 Activity 第一次關聯時調用。onDestroy()
和onDetach()
只在 Fragment 的宿主 Activity 銷毀時才會被調用。- 根據前 3 點,將 Fragment 通過
addToBackStack()
只涉及onCreateView()
和onDestroyView()
這之間的生命周期。add()
和replace()
不會對 Fragment 的生命周期產生影響,但add()
方法會造成 Fragment 疊加顯示。 - Fragment 與 ViewPager 結合使用時的生命周期與第 4 點相似。
- 通過
hide()
、show()
來隱藏、顯示Fragment,此時 Fragment 只改變了可見性,並不涉及生命周期的改變 - 第 7 點與Fragment 和 Activity 的生命周期有關,即:不要在 Fragment 的 onCreate() 方法中操作宿主Activity 的 UI。因為你無法保證此時 宿主Activity 的 UI 已經完全初始化。PS:某些情況下是可以確保 宿主Activity 已經初始化完成的。
恩,的確算淺談,給出了自認為正確的結論,歡迎各位前來打臉~
源碼地址:AndroidDemo/Fragment。歡迎大家 Star 和 Fork。