前提
首先我們要知道匀钧,在系統(tǒng)中有一個專門用來管理Activity實(shí)例對象的棧塘娶,這個堆棧采用的是“先進(jìn)后出”的模式箫爷。我們每創(chuàng)建一個活動對象都會放進(jìn)這個堆棧中酪劫,而最頂端的活動對象才是可見的并且可以和用戶進(jìn)行交互的舀凛!整個堆棧的活動過程如下圖所示:
了解了堆棧之后扼仲,我們再看下官方最新的活動生命周期圖:
其實(shí)我們平常在用APP的時(shí)候看到的APP界面就是以Activity(活動)為基礎(chǔ)的远寸,從而我們也能知道活動的最簡單的功能就是可以給用戶展示界面,并且和用戶交互屠凶!從這個生命周期圖中“visible”,“hidden”等字樣我們也能看出這一現(xiàn)象驰后!
那么我們在可以將活動的狀態(tài)分為:
- 創(chuàng)建(初始化)
- 可見不可交互
- 可見可交互
- 部分可見不可交互
- 不可見不可交互
- 銷毀
在結(jié)合上圖,我們在分別說明一下圖中的7個生命周期方法都有什么作用:
- onCreate() : 活動創(chuàng)建的時(shí)候被調(diào)用矗愧,創(chuàng)建之后之后活動被銷毀了之后再次啟動才會被調(diào)用灶芝。在這個方法中我們通常做一些初始化的工作,例如:控件的初始化,數(shù)據(jù)和控件的綁定等等夜涕!
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
- 拓展說明:從上圖中可以看到方法還帶有一個Bundle類型的參數(shù)犯犁,Bundle類型大家應(yīng)該知道它可以以鍵值對的形式存放許多類型的數(shù)據(jù),所以這個參數(shù)可以用來恢復(fù)一些數(shù)據(jù)女器,至于是什么數(shù)據(jù)酸役,并且這些數(shù)據(jù)是怎么來的,后面會詳情說明的驾胆!
onStart() :執(zhí)行到這個方法的時(shí)候就是我們上面說的“可見不可交互”狀態(tài)涣澡!
onResume():執(zhí)行到這個方法的時(shí)候就是我們上面說的“可見可交互”狀態(tài),這個時(shí)候活動是處于棧頂?shù)模?/p>
onPause():執(zhí)行這個方法的時(shí)候丧诺,活動處于暫停狀態(tài)暑塑,就是我們說的部分可見不可交互狀態(tài),比如:啟動一個對話框模式的活動等锅必,(特別注意:彈出的對話框并不能出發(fā)改方法)事格。從圖中我們可以看到這個方法之后有兩個路徑,向前就是重新展示上一個活動搞隐,向后就是停止當(dāng)前的活動驹愚!
onStop():這個方法代表活動不再可見,也就是此時(shí)活動處于后臺運(yùn)行狀態(tài)劣纲!這個可以有兩個選擇逢捺,要么就是通過onRestart()方法重新打開活動,要么就是執(zhí)行onDestroy()銷毀活動癞季!
-
onRestart():這個方法就是就是用來重新打開活動的劫瞳,不知道這個方法有什么意義,官方給的解釋就是:
當(dāng)您的Activity從停止?fàn)顟B(tài)返回前臺時(shí)绷柒,它會接收對 onRestart() 的調(diào)用志于。系統(tǒng)還會在每次您的Activity變?yōu)榭梢姇r(shí)調(diào)用 onStart() 方法(無論是正重新開始還是初次創(chuàng)建)。 但是废睦,只會在Activity從停止?fàn)顟B(tài)繼續(xù)時(shí)調(diào)用 onRestart() 方法伺绽,因此您可以使用它執(zhí)行只有在Activity之前停止但未銷毀的情況下可能必須執(zhí)行的特殊恢復(fù)工作。
onDestroy():就是銷毀活動的實(shí)例了嗜湃,并且這個活動也將從活動棧的棧頂被清除奈应!
梳理流程:
- 當(dāng)我們啟動活動的時(shí)候會執(zhí)行: 此時(shí)活動可見可交互
- onCreate()
- onStart()
- onResume()
- 之后的操作分兩種情況:
-
如果啟動對話框模式的Activtiy或其他操作,使得當(dāng)前活動部分不可見购披,則只會執(zhí)行:
-
onPause()
-
這種情況之后的操作也分兩種:
- onResume() 執(zhí)行這個方法讓活動重新處于前端
- onStop() 執(zhí)行這個方法讓活動重新處于后臺
-
-
-
如果啟動另一個活動杖挣,使得當(dāng)前活動完全不可見了,則會執(zhí)行:
onPause()
-
onStop()
-
這種情況之后的操作也分兩種:
- onDestroy() 執(zhí)行這個方法銷毀活動
- onRestart()刚陡,onStart()惩妇,onResume() 執(zhí)行這些方法讓重新打開活動
-
需要了解的在生命周期中的一些方法:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
L.e("保存活動狀態(tài)數(shù)據(jù)");
}
這個方法是一定是在onStop()方法之前執(zhí)行在但是不一定在onPause()之后執(zhí)行株汉,時(shí)序不能得到保證。該的方法可以通過Bundle類型的參數(shù)來保存活動的狀態(tài)數(shù)據(jù)屿附。這里保存的數(shù)據(jù)郎逃,可以在onCreate方法中的參數(shù)獲取到哥童!上面已經(jīng)說過了挺份!也可以通過下面的方法獲取到:
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
只要執(zhí)行了這個方法就代表savedInstanceState參數(shù)里一定有數(shù)據(jù),相反只有當(dāng)savedInstanceState參數(shù)里有數(shù)據(jù)才會執(zhí)行這個方法贮懈!但是onCreate方法卻不一定匀泊,至于要在哪個方法里恢復(fù)數(shù)據(jù),其實(shí)都可以朵你!官方推薦是在onRestoreInstanceState這個方法中恢復(fù)數(shù)據(jù)各聘;
上面這兩個方法只會在活動異常終止的情況下執(zhí)行,在正常通過返回鍵銷毀活動的時(shí)候并沒有執(zhí)行該過程抡医。
拓展知識:橫豎屏切換
為什么會說這個呢躲因,因?yàn)樵谑謾C(jī)進(jìn)行橫豎屏切換的時(shí)候,會引發(fā)活動生命周期的一輪變化忌傻,下面我通過打印的log進(jìn)行說明:
首先看下代碼:
在剩下的生命周期方法中我也打印了相關(guān)日志大脉,還有代碼中的參數(shù),之后我啟動程序看打印的日志:
執(zhí)行了相關(guān)的周期方法水孩,并且打印了參數(shù)值镰矿!這時(shí)候我手動旋轉(zhuǎn)手機(jī)變成橫向的再看打印的日志:
從日志可以看出,先是執(zhí)行了onPause(),onStop(),onDestroy(),把當(dāng)前的活動銷毀俘种,之后又重新執(zhí)行了一遍生命周期秤标!
解決這個問題也很簡單,就是在manifests文件生命這個活動的資源里添加這條屬性:
這兩個值解釋下:
orientation:屏幕方向發(fā)生變化宙刘,配置該參數(shù)可以解決橫豎屏切換時(shí)苍姜,Activity重建問題(API<13)
screenSize:當(dāng)設(shè)備旋轉(zhuǎn)時(shí),屏幕尺寸發(fā)生變化悬包,API>13后必須配置該參數(shù)才可以保證橫豎切換不會導(dǎo)致Activity重建怖现。
這樣切屏的時(shí)候就不再回調(diào)
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
而是回調(diào):
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
L.e("onConfigurationChanged");
}
并且不會再引發(fā)生命周期的變化:
** 并且可以通過一下方法來判斷旋轉(zhuǎn)之后的屏幕狀態(tài)是橫屏還是豎屏: **
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
L.e("此時(shí)為橫屏模式");
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
L.e("此時(shí)為豎屏模式");
}
}