概要:
生命週期主要包含 6 種狀態: onCreate, onStart, onResume, onPause, onStop, onDestroy。
各種狀態意義為
onCreate 代表 Activity 的建立:
當系統初次建立 Activity 時 onCreate 方法會被呼叫,此時會進入 Created 狀態,應該在這個方法中實作首次啟動的邏輯,如綁定資料到清單中,關聯ViewModel和Activity,初始化變數。
onCreate方法會傳入 bundle 參數,bundle參數會包含上一次Activity儲存的資料,若Activity之前還未存在過,則bundle參數為null。
當onCreate方法結束後,Activity 會進入 Started 狀態,系統將會呼叫onStart方法。
onStart 代表 Activity 的啟動
當Activity進入Started狀態時,系統將會呼叫 onStart 方法。onStart方法會讓 Activity 變得可見,但還無法和使用者互動。onStart會很快的執行完成,完成後Activity會進入 Resumed 狀態。
onResume 代表 Activity 的恢復
當 Activity 進入 Resumed 狀態,系統將會呼叫onResume方法,onResume方法會讓Activity聚焦並讓使用者可以和Activity互動。App將會停留在這個狀態直到有其它事件取得焦點。
當發生中斷事件時,Activity就會進入Paused狀態,系統便會呼叫onPause方法。
當Activity從 Paused 狀態回到 Resumed 狀態時,系統便會呼叫 onResume 方法
onPause 代表 Activity 的暫停
當Activity失焦(變的部分可見或是透明)系統便會呼叫 onPause方法,此時Activity無法和使用者互動。在onPause中可以考慮釋放一些不需要用到的資源,但在多窗口模式中,Activity依然是完全可見的,釋放資源有可能對Activity造成影響。
若有這種狀況發生,可以考慮在onStop中再釋放資源。
onPause的執行時間很短,不一定有足夠的時間來執行操作。所以不應該在onPause方法中保存資料,進行網路操作,資料庫存取。應該在onStop中進行這些耗時操作。
當 onPause方法結束後並不代表Activity會離開Paused狀態。相反的,Activity會保持在此狀態,直到Activity再次聚焦或完全不可見。
若Activity聚焦則系統會呼叫onResume方法讓Activity回到Resumed狀態,相反的,若Activity變的完全不可見則系統就會呼叫onStop方法讓Activity進入Stopped狀態。
onStop 代表 Activity 的停止
當Activity變得完全不可見(進入後台),系統便會叫onStop方法,Activity進入Stopped狀態。
在onStop方法中應該進行資源的調整或釋放或資料庫的儲存。
當Activity進入Stopped狀態,Activity實體保存在記憶體中,但不會連結到window manager,此時必須小心當系統的記憶體不足時將會銷毀 process。
在Activity為Stopped 狀態時,可以進入2種狀態,當Activity恢復時系統將會呼叫onRestart,若Activity結束時系統將會呼叫onDestroy。
onDestroy 代表 Activity 的銷毀
當Activity銷毀時系統將會呼叫onDestroy,呼叫的原因有2種:
1.當Actiivty結束(使用者關閉Activity或呼叫Activity的finish方法)
2.系統配置發生變化(旋轉螢幕)
在onDestroy方法中應該釋放所有不需要用到資源。
以下為Activity狀態和呼叫方法的圖示
這些狀態分別互相配對,構成 3 種生命週期,分別為完整的生命週期,可視的生命週期,前台的生命週期。
完整的生命週期包含可視的生命週期,可視的生命週期包含前台的生命週期。
完整的生命週期:
Activity 從建立到銷毀的全部過程。最外層生命週期,生命週期發生在 onCreate 到 onDestroy 之間。
可視的生命週期:
Activity 從使用者可視到離開使用者視線的過程。生命週期發生在 onStart 到 onStop 之間。注意可視包含該 Activity 被其它元件遮蓋只顯示一部分的情況。
前台的生命週期:
Activity 顯示在所有的元件之前並可與使用者互動。生命週期發生在 onResume 到 onPause 之間。
另外還有 4 種特殊的生命週期狀態,各為 onRestart, onSaveInstanceState, on RestoreInstanceState, onNewIntent。
onRestart 的呼叫點為從 onStop 到 onStart 之間,也就是 onStop -> onRestart -> onStart。
onSaveInstanceState 用於儲存 Activity 的資料。
onNewIntent 只有在 LaunchMode 為 SingleTop 且啟動的 Activity 為符合重用該 Activity 的規則時才會被呼叫。(onNewIntent -> onResume)
完整的生命週期圖如下
為了實際了解生命週期的變化,建立 FirstActivity 並覆寫所有生命週期印出 Log
FirstActivity.java
public class FirstActivity extends AppCompatActivity implements OnClickListener { private static final String TAG = FirstActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, TAG + " onCreate"); setContentView(R.layout.first_activity); } @Override protected void onStart() { super.onStart(); Log.d(TAG, TAG + " onStart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG, TAG + " onResume"); } @Override protected void onPause() { super.onPause(); Log.d(TAG, TAG + " onPause"); } @Override protected void onStop() { super.onStop(); Log.d(TAG, TAG + " onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, TAG + " onDestroy"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, TAG + " onRestart"); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, TAG + " onSaveInstanceState"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.d(TAG, TAG + " onRestoreInstanceState"); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG, TAG + " onNewIntent"); } }
紀錄1
使用者點擊 back key, home key, menu key 所觸發的生命週期變化:
A.啟動 FirstActivity 後點擊 back key 關閉 FirstActivity
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 back key:
D: FirstActivity onPause
D: FirstActivity onStop
D: FirstActivity onDestroy
B.啟動 FirstActivity 後點擊 home key
1.點擊App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 home key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
C.啟動 FirstActivity 後點擊 home key 再點擊 App icon
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 home key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.點擊 App icon:
D: FirstActivity onRestart
D: FirstActivity onStart
D: FirstActivity onResume
D.啟動 FirstActivity 後點擊 home key 再點擊 menu key 再拖曳移除 App頁面
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 home key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.點擊 menu key,拖曳移除 App:
D: FirstActivity onDestroy(注意有時候不會顯示)
E.啟動 FirstActivit y後點擊 home key 再點擊 menu key 再點擊 App 頁面
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 home key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.點擊 menu key 並點擊 App 頁面:
D: FirstActivity onRestart
D: FirstActivity onStart
D: FirstActivity onResume
F.啟動 FirstActivity後點擊 menu key
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 menu key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
G.啟動 FirstActivity 後點擊 menu key 再拖曳移除 App
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 menu key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.拖曳移除 App:
沒有顯示Log , 推測應該呼叫onDestroy
H.啟動 FirstActivity 後點擊 menu key 再點擊 App頁面
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 menu key:
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.點擊 App 頁面:
D: FirstActivity onRestart
D: FirstActivity onStart
D: FirstActivity onResume
重點:
1.按下 home key 和 menu key 都會觸發 onSaveInstanceState (在 onPause 之後),但按下 back key 不會觸發 onSaveInstanceState。
2.當 Activity 從後台回到前台都會觸發 onRestart -> onStart -> onResume
紀錄2
Activity之間切換的生命週期變化:
2.1修改 FirstActivity,加入啟動 SecondActivity 的功能。
FirstActivity.java
public class FirstActivity extends AppCompatActivity implements OnClickListener { … private Button mLaunchSecondBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, TAG + " onCreate"); setContentView(R.layout.first_activity); initUI(); } private void initUI() { mLaunchSecondBtn = findViewById(R.id.first_activity_launch); mLaunchSecondBtn.setOnClickListener(this); } @Override public void onClick(View v) { int uiID = v.getId(); switch (uiID) { case R.id.first_activity_launch_second_activity: Intent intent = new Intent(this, SecondActivity.class); startActivity(intent); finish(); break; } }
2.2 新增 SecondActivity 和 FirstActivity 互相切換來觀察生命週期。
SecondActivity.java
public class SecondActivity extends AppCompatActivity implements OnClickListener { private static final String TAG = SecondActivity.class.getSimpleName(); private Button mLaunchFirstBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, TAG + " onCreate"); setContentView(R.layout.second_activity); initUI(); } @Override protected void onStart() { super.onStart(); Log.d(TAG, TAG + " onStart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG, TAG + " onResume"); } @Override protected void onPause() { super.onPause(); Log.d(TAG, TAG + " onPause"); } @Override protected void onStop() { super.onStop(); Log.d(TAG, TAG + " onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, TAG + " onDestroy"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, TAG + " onRestart"); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, TAG + " onSaveInstanceState"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.d(TAG, TAG + " onRestoreInstanceState"); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG, TAG + " onNewIntent"); } private void initUI() { mLaunchFirstBtn = findViewById(R.id.second_activity_launch_first_activity); mLaunchFirstBtn.setOnClickListener(this); } @Override public void onClick(View v) { int uiID = v.getId(); switch (uiID) { case R.id.second_activity_launch_first_activity: Intent intent = new Intent(this,FirstActivity.class); startActivity(intent); finish(); break; } } }
A.FirstActivity 啟動 SecondActivity (FirstActivity 不會 finish),再關閉 SecondActivity,再關閉 FirstActivity
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 FirstActivity 的按鈕啟動 SecondActivity。注意 FirstActivity 沒有 finish
D: FirstActivity onPause
D: SecondActivity onCreate
D: SecondActivity onStart
D: SecondActivity onResume
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.點擊 back key 關閉 SecondActivity,讓 FirstActivity 回到前台
D: SecondActivity onPause
D: FirstActivity onRestart
D: FirstActivity onStart
D: FirstActivity onResume
D: SecondActivity onStop
D: SecondActivity onDestroy
4.點擊 back key 關閉 FirstActivity
D: FirstActivity onPause
D: FirstActivity onStop
D: FirstActivity onDestroy
B.FirstActivity 啟動 SecondActivity(FirstActivity 會 finish),再關閉 SecondActivity
1.點擊 App icon 啟動 FristActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 FirstActivity 的按鈕啟動 SecondActivity。注意 FristActivity 會 finish
D: FirstActivity onPause
D: SecondActivity onCreate
D: SecondActivity onStart
D: SecondActivity onResume
D: FirstActivity onStop
D: FirstActivity onDestroy
3.按下 back key 關閉 SecondActivity
D: SecondActivity onPause
D: SecondActivity onStop
D: SecondActivity onDestroy
C.FirstActivity 啟動 SecondActivity(FirstActivity 不會 finish), SecondActivity 再啟動 FirstActivity (SecondActivity不會finish)
1.點擊 App icon 啟動 FristActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 FirstActivity 的按鈕啟動 SecondActivity。注意 FirstActivity 不會 finish
D: FirstActivity onPause
D: SecondActivity onCreate
D: SecondActivity onStart
D: SecondActivity onResume
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
3.點擊 SecondActivity 的按鈕啟動 FirstActivity。注意 SecondActivity 不會 finish
D: SecondActivity onPause
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
D: SecondActivity onSaveInstanceState
D: SecondActivity onStop
D.FirstActivity 啟動 SecondActivity,注意 SecondActivity 使用透明主題(FirstActivity 不會 finish), SecondActivity在啟動 FirstActivity(SecondActivity 不會 finish)
1.點擊 App icon 啟動 FristActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.點擊 FirstActivity 的按鈕啟動 SecondActivity(注意 SecondActivity 使用透明主題注意 FirstActivity不會 finish)
D: FirstActivity onPause
D: SecondActivity onCreate
D: SecondActivity onStart
D: SecondActivity onResume
D: FirstActivity onSaveInstanceState
3.點擊 SecondActivity 的按鈕啟動 FirstActivity。注意 SecondActivity 不會 finish
D: SecondActivity onPause
D: FirstActivity onStop
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
D: SecondActivity onSaveInstanceState
D: SecondActivity onStop
重點:
1.當第 1 個 Activity 啟動第 2 個 Activity時,會讓第 1 個 Activity先呼叫完 onPause,再進行第 2 個 Activity 的初始化(onCreate -> onStart -> onResume)直到第 2 個 Activity 呼叫完 onResume 之後再進行第 1 個 Activity 的後續動作。
2.當第 1 個 Activity 啟動第 2 個 Activity 時,若第 1 個 Activity 會 finish,則 onSaveInstanceState 不會被呼叫。動作順序如下
(第 1 個 Activity 啟動第 2 個 Activity)
Activity1 onPause
Activity2 onCreate
Activity2 onStart
Activity2 onResume
Activity1 onStop
Activity1 onDestroy
反之,若第 1 個Activity 不會 finish,則 onSaveInstanceState 會在 onStop 之前被呼叫。
(第 1 個 Activity 啟動第 2 個 Activity)
Activity1 onPause
Activity2 onCreate
Activity2 onStart
Activity2 onResume
Activity1 onSaveInstanceState
Activity1 onStop
3.如果要啟動的 Activity(SecondActivity)是透明主題,則啟動者(FirstActivity)啟動 SecondActivity 之後不會呼叫 onStop,如下
(當 FirstActivity 啟動 SecondActivity 之後)
D: FirstActivity onPause
D: SecondActivity onCreate
D: SecondActivity onStart
D: SecondActivity onResume
D: FirstActivity onSaveInstanceState
而當 SecondActivity 啟動 FirstActivity之後才會呼叫 onStop 如下
D: SecondActivity onPause
D: FirstActivity onStop
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
D: SecondActivity onSaveInstanceState
D: SecondActivity onStop
紀錄3.
系統配置改變所造成的生命週期變化
當系統配置發生改變,Activity 就會觸發重建過程,最常見的系統配置發生變化為旋轉螢幕。因為這種生命週期變化為系統控制,所以當觸發 onSaveInstanceState 時也會一併呼叫 onRestoreInstanceState 來恢復資料。
A.啟動 FirstActivity 後旋轉螢幕
1.點擊 App icon 啟動 FirstActivity:
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.旋轉螢幕觸發從直屏到橫屏導致 FirstActivity 重建
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
D: FirstActivity onDestroy
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onRestoreInstanceState
D: FirstActivity onResume
3.再次旋轉螢幕從橫屏到直屏導致 FristActivity 重建
D: FirstActivity onPause
D: FirstActivity onSaveInstanceState
D: FirstActivity onStop
D: FirstActivity onDestroy
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onRestoreInstanceState
D: FirstActivity onResume
重點:
1.注意旋轉螢幕之後 onSaveInstanceState 會在 onPause 之後呼叫,而 onRestoreInstanceState 會在 onStart 之後呼叫。
2.若 Activity 要恢復資料可以選擇 onCreate 或 onRestoreInstanceState,差別為於在 onCreate 中需要判斷傳入的參數 Bundle 是否為空,如果是空則不需要恢復資料,而非空才需要進行恢復資料的動作。如下
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) if(savedInstanceState != null){ // restore data } }
3.若要支援旋轉螢幕,但不重建 Activity,可以在 AndroidManifest.xml 中對該 Activity 設定為
android:configChanges="orientation|screenSize"
如此,旋轉螢幕之後 Activity 還是會切換直橫屏,但不會重建,資料也不會初始化,在這種情況下若想在旋轉螢幕時收到通知,可在 Activity 覆寫以下方法。(注意該方法只有在 android:configChanges=”orientation|screenSize” 被設定時才呼叫)。如下
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.d(TAG, "newConfig.toString():"+newConfig.toString()); }
測試生命週期如下
1.點擊 App icon 啟動 FirstActivity
D: FirstActivity onCreate
D: FirstActivity onStart
D: FirstActivity onResume
2.旋轉螢幕從直屏轉為橫屏
D: newConfig.toString():{0 1.0 ?mcc?mnc zh_TW ldltr sw360dp w640dp h335dp
320dpi nrml long land finger -keyb/v/h -nav/h s.47}
3.旋轉螢幕從橫屏轉為直屏
D: newConfig.toString():{0 1.0 ?mcc?mnc zh_TW ldltr sw360dp w360dp h615dp 320dpi nrml long port finger -keyb/v/h -nav/h s.48}
4.若想保持螢幕為直屏或橫屏,可在 AndroidManifest.xml 的 Activity 加入以下
android:screenOrientation="portrait"
螢幕便會保持直屏不會旋轉。
5.注意 onRestoreInstanceState 僅用於系統配置變化時(如旋轉螢幕)導致的重建。如果是使用者導致的重建(點擊 back key or home key)則需要在 onCreate 恢復資料。
注意以上 Activity 的生命週期實驗都是以 LaunchMode (啟動模式)為 default,在其它的啟動模式(SingleTop, SingleTask, SingleInstance)則另外需要另外紀錄。