From Android Training
Android 深入淺出 Activity 生命周期(一)
Android 深入淺出 Activity 生命周期(二)
編寫:kesenhoo
原文:http://developer.android.com/training/basics/activity-lifecycle/index.html
當用戶進入,退出,回到我們的App時,程序中的Activity 實例會在生命周期中的不同狀態間進行切換。例如,activity第一次啟動的時候,它來到系統的前臺,開始接受用戶的焦點。在此期間,Android系統調用了一系列的生命周期中的方法。如果用戶執行了啟動另一個activity或者切換到另一個app(此時雖然當前activity不可見,但其實例與數據仍然存在)的操作, 系統又會調用一些生命周期中的方法。
在生命周期的回調方法中,可以聲明當用戶離開或者重新進入這個Activity所需要執行的操作。例如, 如果我們建立了一個streaming video player, 在用戶切換到另外一個app的時候,應該暫停video 并終止網絡連接。當用戶返回時,我們可以重新建立網絡連接并允許用戶從同樣的位置恢復播放。
本章會介紹一些Activity生命周期中重要的回調方法,如何使用那些方法,使得程序符合用戶的期望且在activity不需要的時候不會導致系統資源的浪費。
完整的Demo示例:ActivityLifecycle.zip
目錄
-
[啟動與銷毀Activity]
學習關于activity生命周期的基礎知識,用戶如何啟動應用及執行基本activity的創建。
-
[暫停與恢復Activity]
學習activity暫停發生時,我們應該做哪些事情。
-
[停止與重啟Activity]
學習用戶離開activity與返回activity時會發生的事情。
-
[重新創建Activity]
學習當我們的activity被銷毀時發生了什么事情,以及在有必要時如何重建我們的activity。
啟動與銷毀Activity
不同于其他編程范式(程序從main()
方法開始啟動),Android系統根據生命周期的不同階段喚起對應的回調函數來執行代碼。系統存在啟動與銷毀一個activity的一套有序的回調函數。
本課介紹生命周期中最重要的回調函數,并演示如何處理啟動一個activity所涉及到的回調函數。
理解生命周期的回調
在一個activity的生命周期中,系統會像金字塔模型一樣去調用一系列的生命周期回調函數。Activity生命周期的每一個階段就像金字塔中的臺階。當系統創建了一個新的activity實例,每一個回調函數會向上一階移動activity狀態。處在金字塔頂端意味著當前activity處在前臺并處于用戶可與其進行交互的狀態。
當用戶退出這個activity時,為了回收該activity,系統會調用其它方法來向下一階移動activity狀態。在某些情況下,activity會隱藏在金字塔下等待(例如當用戶切換到其他app),此時activity可以重新回到頂端(如果用戶回到這個activity)并恢復用戶離開時的狀態。
Figure 1. 這張圖講解了activity的生命周期:(這個金字塔模型要比Dev Guide里面的生命周期圖更加容易理解,更加形象)
根據activity的復雜度,也許不需要實現所有的生命周期方法。但了解每一個方法的回調時機并在其中填充相應功能,使得確保app能夠像用戶期望的那樣執行是很有必要的。如何實現一個符合用戶期待的app,我們需要注意下面幾點:
- 使用app的時候,不會因為有來電通話或者切換到其他app而導致程序crash。
- 用戶沒有激活某個組件時不會消耗寶貴的系統資源。
- 離開app并且一段時間后返回,不會丟失用戶的使用進度。
- 設備發生屏幕旋轉時不會crash或者丟失用戶的使用進度。
下面的課程會介紹上圖所示的幾個生命狀態。然而,其中只有三個狀態是靜態的,這三個狀態下activity可以存在一段比較長的時間。(其它幾個狀態會很快就切換掉,停留的時間比較短暫)
- Resumed:該狀態下,activity處在前臺,用戶可以與它進行交互。(通常也被理解為"running" 狀態)
- Paused:該狀態下,activity的部分被另外一個activity所遮蓋:另外的activity來到前臺,但是半透明的,不會覆蓋整個屏幕。被暫停的activity不再接受用戶的輸入且不再執行任何代碼。
- Stopped:該狀態下, activity完全被隱藏,對用戶不可見。可以認為是在后臺。當stopped, activity實例與它的所有狀態信息(如成員變量等)都會被保留,但activity不能執行任何代碼。
其它狀態 (Created與Started)都是短暫的,系統快速的執行那些回調函數并通過執行下一階段的回調函數移動到下一個狀態。也就是說,在系統調用<a >onCreate()</a>, 之后會迅速調用<a >onStart()</a>, 之后再迅速執行<a >onResume()</a>。以上就是基本的activity生命周期。
指定程序首次啟動的Activity
當用戶從主界面點擊程序圖標時,系統會調用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工具來創建Android工程時,工程中就包含了一個默認的聲明有這個filter的activity類。
如果程序中沒有聲明了MAIN action 或者LAUNCHER category的activity,那么在設備的主界面列表里面不會呈現app圖標。
創建一個新的實例
大多數app包括多個activity,使用戶可以執行不同的動作。不論這個activity是當用戶點擊應用圖標創建的main activtiy還是為了響應用戶行為而創建的其他activity,系統都會調用新activity實例中的onCreate()方法。
我們必須實現onCreate()方法來執行程序啟動所需要的基本邏輯。例如可以在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來避免舊的系統調用了只在Android 2.0(API level 5)或者更新的系統可用的方法(上述if條件中的代碼)。舊的系統調用了這些方法會拋出一個運行時異常。
一旦onCreate 操作完成,系統會迅速調用onStart() 與onResume()方法。我們的activity不會在Created或者Started狀態停留。技術上來說, activity在onStart()被調用后開始被用戶可見,但是 onResume()會迅速被執行使得activity停留在Resumed狀態,直到一些因素發生變化才會改變這個狀態。例如接收到一個來電,用戶切換到另外一個activity,或者是設備屏幕關閉。
在后面的課程中,我們將看到其他方法是如何使用的,onStart() 與 onResume()在用戶從Paused或Stopped狀態中恢復的時候非常有用。
Note: onCreate() 方法包含了一個參數叫做savedInstanceState,這將會在后面的課程 - [重新創建activity]涉及到。
Figure 2. 上圖顯示了onCreate(), onStart() 和 onResume()是如何執行的。當這三個順序執行的回調函數完成后,activity會到達Resumed狀態。
銷毀Activity
activity的第一個生命周期回調函數是 onCreate(),它最后一個回調是<a >onDestroy()</a>.當收到需要將該activity徹底移除的信號時,系統會調用這個方法。
大多數 app并不需要實現這個方法,因為局部類的references會隨著activity的銷毀而銷毀,并且我們的activity應該在onPause()與onStop()中執行清除activity資源的操作。然而,如果activity含有在onCreate調用時創建的后臺線程,或者是其他有可能導致內存泄漏的資源,則應該在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()方法,系統通常是在執行了onPause()與onStop() 之后再調用onDestroy() 。在某些情況下,例如我們的activity只是做了一個臨時的邏輯跳轉的功能,它只是用來決定跳轉到哪一個activity,這樣的話,需要在onCreate里面調用finish方法,這樣系統會直接調用onDestory,跳過生命周期中的其他方法。
暫停與恢復Activity
在正常使用app時,前端的activity有時會被其他可見的組件阻塞(obstructed),從而導致當前的activity進入Pause狀態。例如,當打開一個半透明的activity時(例如以對話框的形式),之前的activity會被暫停。 只要之前的activity仍然被部分可見,這個activity就會一直處于Paused狀態。
然而,一旦之前的activity被完全阻塞并不可見時,則其會進入Stop狀態(將在下一小節討論)。
activity一旦進入paused狀態,系統就會調用activity中的<a >onPause()</a>方法, 該方法中可以停止不應該在暫停過程中執行的操作,如暫停視頻播放;或者保存那些有可能需要長期保存的信息。如果用戶從暫停狀態回到當前activity,系統應該恢復那些數據并執行<a >onResume()</a>方法。
Note: 當我們的activity收到調用onPause()的信號時,那可能意味者activity將被暫停一段時間,并且用戶很可能回到我們的activity。然而,那也是用戶要離開我們的activtiy的第一個信號。
Figure 3. 當一個半透明的activity阻塞activity時,系統會調用onPause()方法并且這個activity會停留在Paused 狀態(1). 如果用戶在這個activity還是在Paused 狀態時回到這個activity,系統則會調用它的onResume() (2).
暫停Activity
當系統調用activity中的onPause(),從技術上講,意味著activity仍然處于部分可見的狀態.但更多時候意味著用戶正在離開這個activity,并馬上會進入Stopped state. 通常應該在onPause()回調方法里面做以下事情:
- 停止動畫或者是其他正在運行的操作,那些都會導致CPU的浪費.
- 提交在用戶離開時期待保存的內容(例如郵件草稿).
- 釋放系統資源,例如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()時執行CPU-intensive 的工作,例如寫數據到DB,因為它會導致切換到下一個activity變得緩慢(應該把那些heavy-load的工作放到<a >onStop()</a>去做)。
如果activity實際上是要被Stop,那么我們應該為了切換的順暢而減少在OnPause()方法里面的工作量。
Note:當activity處于暫停狀態,Activity實例是駐留在內存中的,并且在activity 恢復的時候重新調用。我們不需要在恢復到Resumed狀態的一系列回調方法中重新初始化組件。
恢復activity
當用戶從Paused狀態恢復activity時,系統會調用onResume()方法。
請注意,系統每次調用這個方法時,activity都處于前臺,包括第一次創建的時候。所以,應該實現onResume()來初始化那些在onPause方法里面釋放掉的組件,并執行那些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
}
}