[干貨] Android 深入淺出 Activity 生命周期(一)

From Android Training
Android 深入淺出 Activity 生命周期(一)
Android 深入淺出 Activity 生命周期(二)
編寫:kesenhoo
原文:http://developer.android.com/training/basics/activity-lifecycle/index.html

當用戶進入凿将,退出朗伶,回到我們的App時飞崖,程序中的Activity 實例會在生命周期中的不同狀態(tài)間進行切換穆律。例如特石,activity第一次啟動的時候撵孤,它來到系統(tǒng)的前臺彻消,開始接受用戶的焦點蛔趴。在此期間涤浇,Android系統(tǒng)調用了一系列的生命周期中的方法鳖藕。如果用戶執(zhí)行了啟動另一個activity或者切換到另一個app(此時雖然當前activity不可見,但其實例與數據仍然存在)的操作, 系統(tǒng)又會調用一些生命周期中的方法只锭。

在生命周期的回調方法中著恩,可以聲明當用戶離開或者重新進入這個Activity所需要執(zhí)行的操作。例如, 如果我們建立了一個streaming video player, 在用戶切換到另外一個app的時候蜻展,應該暫停video 并終止網絡連接喉誊。當用戶返回時,我們可以重新建立網絡連接并允許用戶從同樣的位置恢復播放纵顾。

本章會介紹一些Activity生命周期中重要的回調方法伍茄,如何使用那些方法,使得程序符合用戶的期望且在activity不需要的時候不會導致系統(tǒng)資源的浪費施逾。

完整的Demo示例ActivityLifecycle.zip


目錄

  • [啟動與銷毀Activity]

    學習關于activity生命周期的基礎知識敷矫,用戶如何啟動應用及執(zhí)行基本activity的創(chuàng)建。

  • [暫停與恢復Activity]

    學習activity暫停發(fā)生時汉额,我們應該做哪些事情曹仗。

  • [停止與重啟Activity]

    學習用戶離開activity與返回activity時會發(fā)生的事情。

  • [重新創(chuàng)建Activity]

    學習當我們的activity被銷毀時發(fā)生了什么事情蠕搜,以及在有必要時如何重建我們的activity怎茫。


啟動與銷毀Activity

不同于其他編程范式(程序從main()方法開始啟動),Android系統(tǒng)根據生命周期的不同階段喚起對應的回調函數來執(zhí)行代碼讥脐。系統(tǒng)存在啟動與銷毀一個activity的一套有序的回調函數遭居。

本課介紹生命周期中最重要的回調函數啼器,并演示如何處理啟動一個activity所涉及到的回調函數旬渠。

理解生命周期的回調

在一個activity的生命周期中,系統(tǒng)會像金字塔模型一樣去調用一系列的生命周期回調函數端壳。Activity生命周期的每一個階段就像金字塔中的臺階告丢。當系統(tǒng)創(chuàng)建了一個新的activity實例,每一個回調函數會向上一階移動activity狀態(tài)损谦。處在金字塔頂端意味著當前activity處在前臺并處于用戶可與其進行交互的狀態(tài)岖免。

當用戶退出這個activity時岳颇,為了回收該activity,系統(tǒng)會調用其它方法來向下一階移動activity狀態(tài)颅湘。在某些情況下话侧,activity會隱藏在金字塔下等待(例如當用戶切換到其他app),此時activity可以重新回到頂端(如果用戶回到這個activity)并恢復用戶離開時的狀態(tài)。

basic-lifecycle

Figure 1. 這張圖講解了activity的生命周期:(這個金字塔模型要比Dev Guide里面的生命周期圖更加容易理解闯参,更加形象)

根據activity的復雜度瞻鹏,也許不需要實現(xiàn)所有的生命周期方法。但了解每一個方法的回調時機并在其中填充相應功能鹿寨,使得確保app能夠像用戶期望的那樣執(zhí)行是很有必要的新博。如何實現(xiàn)一個符合用戶期待的app,我們需要注意下面幾點:

  • 使用app的時候脚草,不會因為有來電通話或者切換到其他app而導致程序crash赫悄。
  • 用戶沒有激活某個組件時不會消耗寶貴的系統(tǒng)資源。
  • 離開app并且一段時間后返回馏慨,不會丟失用戶的使用進度埂淮。
  • 設備發(fā)生屏幕旋轉時不會crash或者丟失用戶的使用進度。

下面的課程會介紹上圖所示的幾個生命狀態(tài)熏纯。然而同诫,其中只有三個狀態(tài)是靜態(tài)的,這三個狀態(tài)下activity可以存在一段比較長的時間樟澜。(其它幾個狀態(tài)會很快就切換掉误窖,停留的時間比較短暫)

  • Resumed:該狀態(tài)下,activity處在前臺秩贰,用戶可以與它進行交互霹俺。(通常也被理解為"running" 狀態(tài))
  • Paused:該狀態(tài)下,activity的部分被另外一個activity所遮蓋:另外的activity來到前臺毒费,但是半透明的丙唧,不會覆蓋整個屏幕。被暫停的activity不再接受用戶的輸入且不再執(zhí)行任何代碼觅玻。
  • Stopped:該狀態(tài)下, activity完全被隱藏想际,對用戶不可見∠澹可以認為是在后臺胡本。當stopped, activity實例與它的所有狀態(tài)信息(如成員變量等)都會被保留,但activity不能執(zhí)行任何代碼畸悬。

其它狀態(tài) (CreatedStarted)都是短暫的侧甫,系統(tǒng)快速的執(zhí)行那些回調函數并通過執(zhí)行下一階段的回調函數移動到下一個狀態(tài)。也就是說,在系統(tǒng)調用<a >onCreate()</a>, 之后會迅速調用<a >onStart()</a>, 之后再迅速執(zhí)行<a >onResume()</a>披粟。以上就是基本的activity生命周期咒锻。

指定程序首次啟動的Activity

當用戶從主界面點擊程序圖標時,系統(tǒng)會調用app中被聲明為"launcher" (or "main") activity中的onCreate()方法守屉。這個Activity被用來當作程序的主要進入點惑艇。

我們可以在AndroidManifest.xml中定義作為主activity的activity。

這個main activity必須在manifest使用包括 MAIN action 與 LAUNCHER category 的<intent-filter>標簽來聲明拇泛。例如:

<activity android:name=".MainActivity" android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Note:當你使用Android SDK工具來創(chuàng)建Android工程時敦捧,工程中就包含了一個默認的聲明有這個filter的activity類。

如果程序中沒有聲明了MAIN action 或者LAUNCHER category的activity碰镜,那么在設備的主界面列表里面不會呈現(xiàn)app圖標兢卵。

創(chuàng)建一個新的實例

大多數app包括多個activity,使用戶可以執(zhí)行不同的動作绪颖。不論這個activity是當用戶點擊應用圖標創(chuàng)建的main activtiy還是為了響應用戶行為而創(chuàng)建的其他activity秽荤,系統(tǒng)都會調用新activity實例中的onCreate()方法。

我們必須實現(xiàn)onCreate()方法來執(zhí)行程序啟動所需要的基本邏輯柠横。例如可以在onCreate()方法中定義UI以及實例化類成員變量窃款。

例如:下面的onCreate()方法演示了為了建立一個activity所需要的一些基礎操作。如聲明UI元素牍氛,定義成員變量晨继,配置UI等。(onCreate里面盡量少做事情搬俊,避免程序啟動太久都看不到界面)

TextView mTextView; // Member variable for text view in the layout

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Set the user interface layout for this Activity
    // The layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // Initialize member TextView so we can manipulate it later
    mTextView = (TextView) findViewById(R.id.text_message);

    // Make sure we're running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // For the main activity, make sure the app icon in the action bar
        // does not behave as a button
        ActionBar actionBar = getActionBar();
        actionBar.setHomeButtonEnabled(false);
    }
}

Caution:用SDK_INT來避免舊的系統(tǒng)調用了只在Android 2.0(API level 5)或者更新的系統(tǒng)可用的方法(上述if條件中的代碼)紊扬。舊的系統(tǒng)調用了這些方法會拋出一個運行時異常。

一旦onCreate 操作完成唉擂,系統(tǒng)會迅速調用onStart() 與onResume()方法餐屎。我們的activity不會在Created或者Started狀態(tài)停留。技術上來說, activity在onStart()被調用后開始被用戶可見玩祟,但是 onResume()會迅速被執(zhí)行使得activity停留在Resumed狀態(tài)腹缩,直到一些因素發(fā)生變化才會改變這個狀態(tài)。例如接收到一個來電空扎,用戶切換到另外一個activity藏鹊,或者是設備屏幕關閉。

在后面的課程中转锈,我們將看到其他方法是如何使用的盘寡,onStart() 與 onResume()在用戶從Paused或Stopped狀態(tài)中恢復的時候非常有用。

Note: onCreate() 方法包含了一個參數叫做savedInstanceState黑忱,這將會在后面的課程 - [重新創(chuàng)建activity]涉及到宴抚。

basic-lifecycle-create

Figure 2. 上圖顯示了onCreate(), onStart() 和 onResume()是如何執(zhí)行的。當這三個順序執(zhí)行的回調函數完成后甫煞,activity會到達Resumed狀態(tài)菇曲。

銷毀Activity

activity的第一個生命周期回調函數是 onCreate(),它最后一個回調是<a >onDestroy()</a>.當收到需要將該activity徹底移除的信號時,系統(tǒng)會調用這個方法抚吠。

大多數 app并不需要實現(xiàn)這個方法常潮,因為局部類的references會隨著activity的銷毀而銷毀,并且我們的activity應該在onPause()與onStop()中執(zhí)行清除activity資源的操作楷力。然而喊式,如果activity含有在onCreate調用時創(chuàng)建的后臺線程,或者是其他有可能導致內存泄漏的資源萧朝,則應該在OnDestroy()時進行資源清理岔留,殺死后臺線程。

@Override
public void onDestroy() {
    super.onDestroy();  // Always call the superclass

    // Stop method tracing that the activity started during onCreate()
    android.os.Debug.stopMethodTracing();
}

Note: 除非程序在onCreate()方法里面就調用了finish()方法检柬,系統(tǒng)通常是在執(zhí)行了onPause()與onStop() 之后再調用onDestroy() 献联。在某些情況下,例如我們的activity只是做了一個臨時的邏輯跳轉的功能何址,它只是用來決定跳轉到哪一個activity里逆,這樣的話,需要在onCreate里面調用finish方法用爪,這樣系統(tǒng)會直接調用onDestory原押,跳過生命周期中的其他方法。


暫停與恢復Activity

在正常使用app時偎血,前端的activity有時會被其他可見的組件阻塞(obstructed)诸衔,從而導致當前的activity進入Pause狀態(tài)。例如颇玷,當打開一個半透明的activity時(例如以對話框的形式)署隘,之前的activity會被暫停。 只要之前的activity仍然被部分可見亚隙,這個activity就會一直處于Paused狀態(tài)磁餐。

然而,一旦之前的activity被完全阻塞并不可見時阿弃,則其會進入Stop狀態(tài)(將在下一小節(jié)討論)诊霹。

activity一旦進入paused狀態(tài),系統(tǒng)就會調用activity中的<a >onPause()</a>方法, 該方法中可以停止不應該在暫停過程中執(zhí)行的操作渣淳,如暫停視頻播放脾还;或者保存那些有可能需要長期保存的信息。如果用戶從暫停狀態(tài)回到當前activity入愧,系統(tǒng)應該恢復那些數據并執(zhí)行<a >onResume()</a>方法鄙漏。

Note: 當我們的activity收到調用onPause()的信號時嗤谚,那可能意味者activity將被暫停一段時間,并且用戶很可能回到我們的activity怔蚌。然而巩步,那也是用戶要離開我們的activtiy的第一個信號。

basic-lifecycle-paused

Figure 3. 當一個半透明的activity阻塞activity時桦踊,系統(tǒng)會調用onPause()方法并且這個activity會停留在Paused 狀態(tài)(1). 如果用戶在這個activity還是在Paused 狀態(tài)時回到這個activity椅野,系統(tǒng)則會調用它的onResume() (2).

暫停Activity

當系統(tǒng)調用activity中的onPause(),從技術上講籍胯,意味著activity仍然處于部分可見的狀態(tài).但更多時候意味著用戶正在離開這個activity竟闪,并馬上會進入Stopped state. 通常應該在onPause()回調方法里面做以下事情:

  • 停止動畫或者是其他正在運行的操作,那些都會導致CPU的浪費.
  • 提交在用戶離開時期待保存的內容(例如郵件草稿).
  • 釋放系統(tǒng)資源杖狼,例如broadcast receivers, sensors (比如GPS), 或者是其他任何會影響到電量的資源炼蛤。

例如, 如果程序使用Camera,onPause()會是一個比較好的地方去做那些釋放資源的操作。

@Override
public void onPause() {
    super.onPause();  // Always call the superclass method first

    // Release the Camera because we don't need it when paused
    // and other activities might need to use it.
    if (mCamera != null) {
        mCamera.release()
        mCamera = null;
    }
}

通常蝶涩,不應該使用onPause()來保存用戶改變的數據 (例如填入表格中的個人信息) 到永久存儲(File或者DB)上鲸湃。僅僅當確認用戶期待那些改變能夠被自動保存的時候(例如正在撰寫郵件草稿),才把那些數據存到永久存儲 子寓。但是暗挑,我們應該避免在onPause()時執(zhí)行CPU-intensive 的工作,例如寫數據到DB斜友,因為它會導致切換到下一個activity變得緩慢(應該把那些heavy-load的工作放到<a >onStop()</a>去做)炸裆。

如果activity實際上是要被Stop,那么我們應該為了切換的順暢而減少在OnPause()方法里面的工作量鲜屏。

Note:當activity處于暫停狀態(tài)烹看,Activity實例是駐留在內存中的,并且在activity 恢復的時候重新調用洛史。我們不需要在恢復到Resumed狀態(tài)的一系列回調方法中重新初始化組件惯殊。

恢復activity

當用戶從Paused狀態(tài)恢復activity時,系統(tǒng)會調用onResume()方法也殖。

請注意土思,系統(tǒng)每次調用這個方法時,activity都處于前臺忆嗜,包括第一次創(chuàng)建的時候己儒。所以,應該實現(xiàn)onResume()來初始化那些在onPause方法里面釋放掉的組件捆毫,并執(zhí)行那些activity每次進入Resumed state都需要的初始化動作 (例如開始動畫與初始化那些只有在獲取用戶焦點時才需要的組件)

下面的onResume()的例子是與上面的onPause()例子相對應的闪湾。

@Override
public void onResume() {
    super.onResume();  // Always call the superclass method first

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) {
        initializeCamera(); // Local method to handle camera init
    }
}

Android 深入淺出 Activity 生命周期(二)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绩卤,隨后出現(xiàn)的幾起案子途样,更是在濱河造成了極大的恐慌江醇,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件何暇,死亡現(xiàn)場離奇詭異陶夜,居然都是意外死亡,警方通過查閱死者的電腦和手機赖晶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辐烂,“玉大人遏插,你說我怎么就攤上這事【佬蓿” “怎么了胳嘲?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扣草。 經常有香客問我了牛,道長,這世上最難降的妖魔是什么辰妙? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任鹰祸,我火速辦了婚禮,結果婚禮上密浑,老公的妹妹穿的比我還像新娘蛙婴。我一直安慰自己,他們只是感情好尔破,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布街图。 她就那樣靜靜地躺著,像睡著了一般懒构。 火紅的嫁衣襯著肌膚如雪餐济。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天胆剧,我揣著相機與錄音絮姆,去河邊找鬼。 笑死秩霍,一個胖子當著我的面吹牛滚朵,可吹牛的內容都是我干的。 我是一名探鬼主播前域,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辕近,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了匿垄?” 一聲冷哼從身側響起移宅,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤归粉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漏峰,有當地人在樹林里發(fā)現(xiàn)了一具尸體糠悼,經...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年浅乔,在試婚紗的時候發(fā)現(xiàn)自己被綠了倔喂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡靖苇,死狀恐怖席噩,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情贤壁,我是刑警寧澤悼枢,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站脾拆,受9級特大地震影響馒索,放射性物質發(fā)生泄漏。R本人自食惡果不足惜名船,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一绰上、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渠驼,春花似錦渔期、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谋梭,卻和暖如春信峻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓮床。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工盹舞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人隘庄。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓踢步,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丑掺。 傳聞我的和親對象是個殘疾皇子获印,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內容