Activity是Android程式的4大元件之一,是Android程式的表示層。程式的每一個顯示螢幕就是一個Activity,它上面可以顯示一些控制元件也可以監聽並處理使用者的事件做出響應,Activity之間透過Intent進行通訊。
一、Activity 的生命週期:
為了理解生命週期,先說一下任務:任務其實就是“應用程式”,它是由一個或多個Activity組成的共同完成一個完整的使用者體驗。
比如我們開啟QQ首先進入聯絡人列表介面,這個介面就是一個Activity:
點選“新增好友”便進入到搜尋的Activity:
點選一條搜尋結果便進入到了名片的Activity:
一個任務的Activity是由棧的形式儲存的,棧底的是啟動整個任務的Activity,棧頂的是當前執行的使用者可以互動的Activity,當一個activity 啟動另外一個的時候,新的activity 就被壓入棧,併成為當前執行的activity。而前一個activity 仍保持在棧之中。當使用者按下BACK 鍵的時候,當前activity 出棧,而前一個恢復為當前執行的activity。棧中的Activity 永遠不會重排,只會壓入或彈出。
任務中的所有Activity 是作為一個整體進行移動的。整個的任務(即Activity 棧)可以移到前臺,或退至後臺。舉個例子說,比如當前任務在棧中存有四個Activity──三個在當前Activity 之下。當使用者按下HOME 鍵的時候,回到了系統介面,然後選擇了一個新的應用程式(也就是一個新任務)。則當前任務轉入後臺,而新任務的根Activity 顯示出來。然後,使用者再次回到了系統介面選擇了前一個應用程式(上一個任務)。於是那個任務,帶著它棧中所有的四個Activity,再一次的到了前臺。當使用者按下BACK 鍵的時候,螢幕不會回到使用者剛才離開的任務。取而代之,當前任務的棧中最上面的Activity 被彈出,倒數第二個Activity 顯示了出來。
Android系統是一個多工(Multi-Task)的作業系統,可以在用手機聽音樂的同時執行其他多個程式。每多執行一個應用程式,就會多耗費一些系統記憶體,當同時執行的程式過多,或是關閉的程式沒有正確釋放掉記憶體,系統就會覺得越來越慢,甚至不穩定。為了解決這個問題, Android 引入了一個新的機制-- 生命週期(Life Cycle)。Android 應用程式的生命週期是由Android 框架進行管理,而不是由應用程式控制。通常,每一個應用程式(入口一般會是一個Activity 的onCreate 方法),都會產生一個程式(Process)。當系統記憶體即將不足的時候,會依照優先順序自動進行程式(process)的回收。不管是使用者或開發者, 都無法確定的應用程式何時會被回收。所以為了防止資料丟失和其他問題,瞭解生命週期很重要。
1、四種狀態:
1.活動(Active/Running)狀態:
當Activity執行在螢幕前臺(處於當前任務活動棧的最上面),此時它獲取了焦點能響應使用者的操作,屬於執行狀態,同一個時刻只會有一個Activity 處於活動(Active)或執行(Running)狀態。
2.暫停(Paused)狀態:
當Activity失去焦點但仍對使用者可見(如在它之上有另一個透明的Activity或Toast、AlertDialog等彈出視窗時)它處於暫停狀態。暫停的Activity仍然是存活狀態(它保留著所有的狀態和成員資訊並保持和視窗管理器的連線),但是當系統記憶體極小時可以被系統殺掉。
3.停止(Stopped)狀態:
完全被另一個Activity遮擋時處於停止狀態,它仍然保留著所有的狀態和成員資訊。只是對使用者不可見,當其他地方需要記憶體時它往往被系統殺掉。
4.非活動(Dead)狀態:
Activity 尚未被啟動、已經被手動終止,或已經被系統回收時處於非活動的狀態,要手動終止Activity,可以在程式中呼叫"finish"方法。如果是(按根據記憶體不足時的回收規則)被系統回收,可能是因為記憶體不足了。
記憶體不足時,Dalvak 虛擬機器會根據其記憶體回收規則來回收記憶體:
1.先回收與其他Activity 或Service/Intent Receiver 無關的程式(即優先回收獨立的Activity)因此建議我們的一些(耗時)後臺操作,最好是作成Service的形式。
2.不可見(處於Stopped狀態的)Activity。
3.Service程式(除非真的沒有記憶體可用時會被銷燬)。
4.非活動的可見的(Paused狀態的)Activity。
5.當前正在執行(Active/Running狀態的)Activity。
2、七個重要方法,當Activity從一種狀態進入另一狀態時系統會自動呼叫下面相應的方法來通知使用者這種變化:
1.onCreate(Bundle savedInstanceState):
當Activity第一次被例項化的時候系統會呼叫,整個生命週期只呼叫1次這個方法通常用於初始化設定,為Activity設定所要使用的佈局檔案,為按鈕繫結監聽器等靜態的設定操作。
2.onStart():
當Activity可見未獲得使用者焦點不能互動時系統會呼叫。
3.onRestart():
當Activity已經停止然後重新被啟動時系統會呼叫。
4.onResume():
當Activity可見且獲得使用者焦點能互動時系統會呼叫。
5.onPause():
當系統啟動另外一個新的Activity時,在新Activity啟動之前被系統呼叫儲存現有的Activity中的持久資料、停止動畫等,這個實現方法必須非常快。當由系統而不是使用者自己出於回收記憶體時,關閉了Activity 之後。使用者會期望當他再次回到這個Activity 的時候,它仍保持著上次離開時的樣子。此時用到了onSaveInstanceState(),方法onSaveInstanceState()用來儲存Activity被殺之前的狀態,在onPause()之前被觸發,當系統為了節省記憶體銷燬了Activity(使用者本不想銷燬)時就需要重寫這個方法了,當此Activity再次被例項化時會透過onCreate(Bundle savedInstanceState)將已經儲存的臨時狀態資料傳入。因為onSaveInstanceState()方法不總是被呼叫,因為有些場景是不需要儲存狀態資料的。 比如使用者按下BACK鍵退出activity時, 使用者顯然想要關閉這個activity,此時是沒有必要儲存資料以供下次恢復的,也就是onSaveInstanceState()方法不會被呼叫。onSaveInstanceState()方法觸發條件為(按下HOME鍵,按下電源按鍵關閉螢幕,橫豎屏切換情況下),應該僅重寫onSaveInstanceState()來記錄Activity的臨時狀態,而不是持久的資料。應該使用onPause()來儲存持久資料。
6.onStop():
當Activity被新的Activity完全覆蓋不可見時被系統呼叫。
7.onDestroy():
當Activity(使用者呼叫finish()或系統由於記憶體不足)被系統銷燬殺掉時系統呼叫,(整個生命週期只呼叫1次)用來釋放onCreate ()方法中建立的資源,如結束執行緒等。
3、三個巢狀迴圈
1.Activity完整的生命週期:從第一次呼叫onCreate()開始直到呼叫onDestroy()結束
2.Activity的可視生命週期:從呼叫onStart()到相應的呼叫onStop()在這兩個方法之間,可以保持顯示Activity所需要的資源。如在onStart()中註冊一個廣播接收者監聽影響你的UI的改變,在onStop() 中登出。
3.Activity的前臺生命週期:從呼叫onResume()到相應的呼叫onPause()。
舉例說明:
有3個Acitivity,分別用One,Two(透明的),Three表示,One是應用啟動時的主Activity;
啟動第一個介面Activity One時,它的次序是:
onCreate (ONE) - onStart (ONE) - onResume(ONE)
點"開啟透明Activity"按鈕時,這時走的次序是:
onPause(ONE) - onCreate(TWO) - onStart(TWO) - onResume(TWO)
再點back回到第一個介面,Two會被殺這時走的次序是:
onPause(TWO) - onActivityResult(ONE) - onResume(ONE) - onStop(TWO) - onDestroy(TWO)
點"開啟全屏Activity"按鈕時,這時走的次序是:
onPause(ONE) - onCreate(Three) - onStart(Three) - onResume(Three) - onStop(ONE)
再點back回到第一個介面,Three會被殺這時走的次序是:
onPause(Three) - onActivityResult(ONE) - onRestart(ONE) - onStart(ONE)- onResume(ONE) - onStop(Three) - onDestroy(Three)
再點back退出應用時,它的次序是:
onPause(ONE) - onStop(ONE) - onDestroy(ONE)
二、程式碼示例:
新建一個測試專案,並在MainActivity寫以下程式碼:
/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Toast.makeText(this, "onCreate(Bundle savedInstanceState)方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onStart(){ super.onStart(); Toast.makeText(this, "onStart()方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onRestoreInstanceState(Bundle savedInstanceState){ super.onRestoreInstanceState(savedInstanceState); Toast.makeText(this, "onRestoreInstanceState(Bundle savedInstanceState)方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onRestart(){ super.onRestart(); Toast.makeText(this, "onRestart()方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onSaveInstanceState(Bundle savedInstanceState){ super.onSaveInstanceState(savedInstanceState); Toast.makeText(this, "onSaveInstanceState(Bundle savedInstanceState)方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onPause(){ super.onPause(); Toast.makeText(this, "onPause()方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onResume(){ super.onResume(); Toast.makeText(this, "onResume方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onStop(){ super.onStop(); Toast.makeText(this, "onStop()方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onDestroy(){ super.onDestroy(); Toast.makeText(this, "onDestroy()方法被呼叫了。", Toast.LENGTH_SHORT).show(); } @Override public void onConfigurationChanged(Configuration newConfig){ super.onConfigurationChanged(newConfig); Toast.makeText(this, "onConfigurationChanged()方法被呼叫了。", Toast.LENGTH_SHORT).show(); }
執行後在模擬器上觀察方法的呼叫情況。
三、橫豎屏切換時候Activity的生命週期:
1.預設設定切換時的生命週期:
執行剛才新建的測試專案,Toast訊息的提示呼叫順序是:
“onCreate”->"onStart"->"onResume"
按crtl+f12切換模擬器為橫屏時,Toast訊息的提示呼叫順序是:
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"
再次按按crtl+f12切換模擬器為豎屏時,發現Toast訊息顯示了2次相同的呼叫順序:
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"->
"onSaveInstanceState"->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"
2.修改AndroidManifest.xml,在該Activity新增android:configChanges="orientation":
<activity android:name=".MainActivityLife" android:label="@string/app_name" android:configChanges="orientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
重新生成並執行專案,然後按crtl+f12切換模擬器為橫屏時,Toast訊息的提示呼叫順序是:
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"
再次按按crtl+f12切換模擬器為豎屏時,Toast訊息的提示多了一個"onConfigurationChanged":
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"->"onConfigurationChanged"
3.修改AndroidManifest.xml,把剛才新增的android:configChanges="orientation"改為android:configChanges="keyboardHidden|orientation":
<activity android:name=".MainActivityLife" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
重新生成並執行專案,然後按crtl+f12切換模擬器為橫屏時,Toast訊息只提示了"onConfigurationChanged":
"onConfigurationChanged"
再次按按crtl+f12切換模擬器為豎屏時,Toast訊息提示了2次"onConfigurationChanged":
"onConfigurationChanged"->"onConfigurationChanged"
4.總結:
不設定Activity的android:configChanges時,切屏會重新呼叫各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次。
設定Activity的android:configChanges="orientation"時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次,且在豎屏時執行了一次"onConfigurationChanged"。
設定Activity的android:configChanges="keyboardHidden|orientation"時,切屏不會重新呼叫各個生命週期,只會執行"onConfigurationChanged"。