Activity 的生命週期
作者: gasolin
日期: 2010-03-03
本文详细介绍了 Activity 的生命周期,非常明了。
|
生命週期作者在初級章節中一直努力地傳達給讀者:編寫 Android 平台的基本應用程式,跟編寫桌面應用程式的難度,兩者並沒什麼不同。甚至因為 Android 平台擁有免費、跨平台的開發工具,使得 Android 平台應用程式的開發更為單純。 但是請別忘了,Android 平台也是個手機作業系統。撇掉其他功能不談,手機的特性,就是應該能隨時在未完成目前動作的時候,離開正在使用的功能,切換到接電話、接收簡訊模式...而且在接完電話回來應用程式時,還希望能看到一樣的內容。 現在使用者使用智慧型手機,大多已習慣使用多工(Multi-Task)的作業系統(如 Windows Mobile),可以在用手機聽音樂的同時,也執行其他多個程式。同時執行多個程式有它的明顯好處,但是也有它的嚴重的缺點。每多執行一個應用程式,就會多耗費一些系統記憶體。而手機裡的記憶體是相當有限的。當同時執行的程式過多,或是關閉的程式沒有正確釋放掉記憶體,執行系統時就會覺得越來越慢,甚至不穩定。 為了解決這個問題, Android 引入了一個新的機制 — 生命週期 (Life Cycle)。 行程應用程式(一個個 Activity)執行的狀態稱為行程(process)。在 Android 作業系統中,每個應用程式都是一個行程。Android 系統平台(準確的說是 Dalvik 虛擬機)會維護一個唯一的 Activity 歷史記錄堆疊,並從旁觀察每個應用程式行程。系統平台會依照系統的記憶體狀況,與 Activity 的使用狀態,來管理記憶體的使用。 Activity 類別除了負責運行程式流程,與操作介面元件之外,最重要的,就是它提供了開發者控制行程生命週期的函式。我們已經相當習慣在 OnCreate (建立行程時的行為)函式中,加入我們對這個 Activity 執行流程的控制。在前面遇到的範例中,我們並不需要對除了 OnCreate 之外的行為做出改變。不過理解行程的生命週期,將為我們繼續深入 Android 開發打下基礎。 為什麼要了解生命週期Android 應用程式的生命週期是由 Android 框架進行管理,而不是由應用程式直接控制。 通常,每一個應用程式(入口一般會是一個 Activity 的 onCreate 方法),都會佔據一個行程(Process)。當系統記憶體即將不足的時候,會依照優先級自動進行行程(process)的回收。不管是使用者或開發者,都無法確定的應用程式何時會被回收。 一個 Activity 類別除了 OnCreate 函式之外,還預先定義了 OnPause(暫停行程時的行為)、OnResume(繼續行程時的行為)等等的基本行為,當從一個 Activity 切換到另一個 Activity 的時候,原本的 Activity 將經過一連串的狀態改變。開發者可以在程式中添加一些各狀態相對應的流程,每次 Activity 改變狀態時,就會執行相對應的流程。 要讓使用者有好的使用經驗,Activity 需要在各個週期點上負責保管狀態、恢復狀態、傳送資料等工作。 Activity 的狀態Android 的虛擬機(VM)是使用堆疊 (Stack based) 管理。主要有四種狀態:
Active (活動)「Active」狀態是使用者啟動應用程式或 Activity 後,Activity 運行中的狀態。 在 Android 平台上,同一個時刻只會有一個 Activity 處於活動(Active)或運行(Running)狀態。其他的 Activity 都處於未啟動(Dead)、停止(Stopped)、或是暫停(Pause)的狀態。 Paused (暫停)「Paused」狀態是當 Activity 暫時暗下來,退到背景畫面的狀態。 當我們使用Toast、AlertDialog、或是電話來了時,都會讓原本運行的 Activity 退到背景畫面。新出現的Toast、AlertDialog等介面元件蓋住了原來的 Activity 畫面。Activity 處在「Paused」狀態時,使用者無法與原 Activity 互動。 Stopped (停止)「Stopped」狀態是有其他 Activity 正在執行,而這個 Activity 已經離開螢幕,不再動作的狀態。 透過長按「Home」鈕,可以叫出所有處於「Stopped」狀態的應用程式列表。 在「Stopped」狀態的 Activity,還可以透過「Notification」來喚醒。「Notification」會在後面章節中解說。 Dead (已回收或未啟動)「Dead」狀態是 Activity 尚未被啟動、已經被手動終止,或已經被系統回收的狀態。 要手動終止 Activity,可以在程式中呼叫「finish」函式。 如果是被系統回收,可能是因為記憶體不足了,所以系統根據記憶體不足時的回收規則,將處於「Stopped」狀態的 Activity 所佔用的記憶體回收。 記憶體不足時的行為記憶體不足時,Dalvik 虛擬機會根據其記憶體回收規則來回收記憶體:
當系統缺記憶體缺到開始「4. 關掉可見的 Activity/行程」時,大概我們換機子的時機也早該到啦! 觀察 Activity 運作流程講了這麼多虛的,我們可以寫一些程式來直觀查看 Activity 的運作流程嗎? 當然可以。在上一章記錄與偵錯 (Log)中,我們學到的「Log」工具,正好可以在查看 Activity 的運作流程時派上用場。 打開「src/com/demo/android/bmi/Bmi.java」,在程式中加入一些「Log」記錄點: public class Bmi extends Activity { private static final String TAG = "Bmi"; public void onCreate() { super.onCreate(...); Log.v(TAG,"onCreate"); } public void onStart() { super.onStart(); Log.v(TAG,"onStart"); } public void onResume() { super.onResume(); Log.v(TAG,"onResume"); } public void onPause() { super.onPause(); Log.v(TAG,"onPause"); } public void onStop() { super.onStop(); Log.v(TAG,"onStop"); } public void onRestart() { super.onRestart(); Log.v(TAG,"onReStart"); } public void onDestroy() { super.onDestroy(); Log.v(TAG,"onDestroy"); } } 講解我們為 Activity 的各個狀態加入了「Log」記錄訊息。當模擬器運行時,我們可以透過 「LogCat」工具來查看 Activity 所處在的狀態。 上面的七個狀態又可以歸納成三組: 1. 資源分配 (Create/Destroy) 完整的 Activity 生命週期由「Create」狀態開始,由「Destroy」狀態結束。建立(Create)時分配資源,銷毀(Destroy)時釋放資源。 2. 可見與不可見(Start/ReStart/Stop) 當 Activity 運行到「Start」狀態時,就可以在螢幕上看到這個 Activity。相反地,當Activity 運行到「Stop」狀態時,這個 Activity 就會從螢幕上消失。 當使用者按下 Back 按鈕回到上一個 Activity 時,會先到 Restart 狀態,再到一般的 Start 狀態。 3. 使用者能否直接存取螢幕(Resume/Pause) 當有個 Toast、AlertDialog、簡訊、電話等訊息亂入時,原來的 Activity 會進入「Pause」狀態,暫時放棄直接存取螢幕的能力,被中斷到背景去,將前景交給優先級高的事件。當這些優先級高的事件處理完後,Activity 就改進入「Resume」狀態,此時又直接存取螢幕。 Activity 運作流程由實際運行的記錄來看,我們可以歸納出所有 Android 應用程式都遵循的動作流程: 一般啟動onCreate -> onStart -> onResume 啟動一個 Activity 的基本流程是:分配資源給這個 Activity(Create 狀態),然後將 Activity 內容顯示到螢幕上(Start 狀態)。在一切就緒後,取得螢幕的控制權(Resume 狀態),使用者可以開始使用這個程式。 呼叫另一個 ActivityonPause(1) -> onCreate(2) -> onStart(2) - onResume(2) -> onStop(1) 這是個先凍結原本的 Activity,再交出直接存取螢幕能力(Pause 狀態)的過程。 直到 Activity 2 完成一般啟動流程後,Activity 1 才會被停止。 回原 ActivityonPause(2) -> onRestart(1) -> onStart(1) -> onResume(1) -> onStop(2) -> onDestroy(2) 點 Back 按鈕可以回到原本的 Activity。 退出結束onPause -> onStop -> onDestroy 如果程式中有直接呼叫「finish」函式來關閉 Activity的話,系統假設我們很確定我們在做什麼,因此會直接跳過先凍結(Freeze)的階段,暫停(Pause),停止(Stop),然後銷毀(Destroy)。 回收後再啟動onCreate -> onStart -> onResume 被回收掉的 Activity 一旦又重新被呼叫時,會像一般啟動一樣再次呼叫 Activity 的 onCreate 函式。 當我們使用「Android」手機一陣子,在手機上已經執行過多個應用程式。只要按下「Back」(返回)鍵,「Android」就會開啟最近一次開啟過的 Activity。 這時我們要是按下多次「Back」(返回)鍵,理論上遲早會返回到某個已經銷毀(Destroy)的 Activity。這時會發生什麼事呢? 如果應該開啟的 Activity 已經被回收了,那麼這個 Activity 會再次被建立(Create)出來。再次被建立出來的 Activity,當然會跟原本我們開啟過的 Activity 不一樣啦。 所以如果要讓再次被建立出來的 Activity 看起來跟原本開啟過的一樣,那麼在 Activity 之間切換時,我們就要留意保留資料:最好在每次 Activity 運行到「onPause」或「onStop」狀態時先保存資料,然後在「onCreate」時將資料讀出來。 Back 按键的处理当按下 Back 按钮时,将进程杀掉,虽然这种方法比较野蛮,但也是一种办法。流程如下所示: onKeyBackDown -> finish() -> onDestroy -> kill process 示例代码如下所示: public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { finish (); } } @Override protected void onDestroy() { super.onDestroy(); //Kill myself android.os.Process.killProcess(android.os.Process.myPid()); } 参考资料
原文 |
对手机有所研究的朋友们,应该对于Android这一手机操作系统并不陌生。它是一款基于Linux平台的开源手机操作系统。我们在这里会通过对Android Activity生命周期的介绍来充分的了解一下这一系统的基本原理。
注意到在Activity的API中有大量的onXXXX形式的函数定义,除了我们前面用到的onCreate以外,还有onStart,onStop以及onPause等等。从字面上看,它们是一些事件回调,那么次序又是如何的呢?其实这种事情,自己做个实验最明白不过了。在做这个实验之前,我们先得找到在Android中的Log是如何输出的。
显然,我们要用的是android.util.log类,这个类相当的简单易用,因为它提供的全是一些静态方法:
- Log.v(String tag, String msg); //VERBOSE
- Log.d(String tag, String msg); //DEBUG
- Log.i(String tag, String msg); //INFO
- Log.w(String tag, String msg); //WARN
- Log.e(String tag, String msg); //ERROR
前面的tag是由我们定义的一个标识,一般可以用“类名_方法名“来定义。
输出的LOG信息,如果用Eclipse+ADT开发,在LogCat中就可以看到,否则用adb logcat也行,不过我是从来都依赖于IDE环境的。
好了,现在我们修改前面的HelloThree代码:
- public void onStart()
- ...{
- super.onStart();
- Log.v(TAG,"onStart");
- }
- public void onStop()
- ...{
- super.onStop();
- Log.v(TAG,"onStop");
- }
- public void onResume()
- ...{
- super.onResume();
- Log.v(TAG,"onResume");
- }
- public void onRestart()
- ...{
- super.onRestart();
- Log.v(TAG,"onReStart");
- }
- public void onPause()
- ...{
- super.onPause();
- Log.v(TAG,"onPause");
- }
- public void onDestroy()
- ...{
- super.onDestroy();
- Log.v(TAG,"onDestroy");
- }
- public void onFreeze(Bundle outState)
- ...{
- super.onFreeze(outState);
- Log.v(TAG,"onFreeze");
- }
在HelloThreeB中也同样增加这样的代码,编译,运行一下,从logcat中分析输出的日志。
在启动第一个界面Activity One时,它的次序是:
- onCreate (ONE) - onStart (ONE) - onResume(ONE)
虽然是第一次启动,也要走一遍这个resume事件。然后,我们点goto跳到第二个Activity Two中(前一个没有关闭),这时走的次序是:
- onFreeze(ONE) - onPause(ONE) - onCreate(TWO) - onStart(TWO) - onResume(TWO) - onStop(ONE)
说明,第二个Activity Two在启动前,One会经历一个:冻结、暂停的过程,在启动Two后,One才会被停止?
然后,我们再点back回到第一个界面,这时走的次序是:
- onPause(TWO) - onActivityResult(ONE) - onStart(ONE) - onRestart(ONE) - onResume(ONE) - onStop(TWO) - onDestroy(TWO)
说明,返回时,Two没有经历冻结就直接暂停了,在One接收参数,重启后,Two就停止并被销毁了。最后,我们点一下Exit退出应用,它的次序是:
- onPause(ONE) - onStop(ONE) - onDestroy(ONE)
说明如果我们用了finish的话,不会有freeze,但是仍会经历pause - stop才被销毁。
这里有点疑问的是:为什么回来时先是Start才是Restart?可是文档中的图上画的却是先restart再start的啊?不过,后面的表格中的描述好象是正确的,start后面总是跟着resume(如果是第一次)或者restart(如果原来被stop掉了,这种情况会在start与resume 中插一个restart)。