每一個活動在其生命週期中最多可能會有 4 種狀態:java
一、運行狀態android
當一個活動位於返回棧的棧頂時,這時活動就處於運行狀態。系統最不肯意回收的就是處於運行狀態的活動,由於這會帶來很是差的用戶體驗。app
二、暫停狀態ide
當一個活動再也不處於棧頂位置,但仍然可見時,這時活動就進入了暫停狀態。你可能會以爲既然活動已經不在棧頂了,還怎麼會可見呢?佈局
這是由於並非每個活動都會佔滿整個屏幕,好比對話框形式的活動只會佔用屏幕中間的部分區域。處於暫停狀態的活動仍然是徹底存活着的,系統也不肯意去回收這種活動(由於它仍是可見的,回收可見的東西都會在用戶體驗方面有很差的影響,)只有在內存極低的狀況下,系統纔會去考慮回收這種活動。this
三、中止狀態spa
當一個活動再也不處於棧頂位置,而且徹底不可見的狀態,就進入了中止狀態。系統仍然會爲這種活動保持相應的狀態和成員變量,可是這並非徹底可靠的,當其餘地方須要內存時,處於中止狀態的活動有可能會被系統回收。code
四、銷燬狀態orm
當一個活動從返回棧種移除後就變成了銷燬狀態。系統會最傾向於回收處於這種狀態的活動,從而保證手機的內存充足。xml
Activity 類中定義了 7 個回調方法,覆蓋了 Activity 生命週期的每個環節:
onCreate()
這個方法你已經看到過不少次了,每一個活動中咱們都重寫了這個方法,它會在活動第一次被建立的時候調用。你應該在這個方法中完成活動的初始化操做,好比加載佈局、綁定事件等。
onStart()
這個方法在活動由不可見變爲可見的時候調用。
onResume()
這個方法在活動準備好和用戶進行交互的時候調用。此時的活動必定位於返回棧的棧頂,而且處於運行狀態。
onPause()
這個方法在系統準備去啓動或者恢復另外一個活動的時候調用。咱們一般會在這個方法中將一些消耗 CPU 的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度必定要快,否則會影響到新的棧頂活動的使用。
onStop()
這個方法在活動徹底不可見的時候調用。它和 onPause() 方法的主要區別在於,若是啓動的新活動是一個對話框式的活動,那麼 onPause() 方法會獲得執行,而 onStop() 方法並不會執行。
onDestroy()
這個方法在活動被銷燬以後調用,以後活動的狀態將變爲銷燬狀態。
onRestart()
這個方法在活動由中止狀態變爲運行狀態以前調用,也就是活動被從新啓動了。
以上 7 個方法中除了 onRestart() 方法,其餘都是兩兩對應的,從而能夠將活動分爲 3 種生存期。
完整生存期:活動在 onCreate() 方法和 onDestroy() 方法之間所經歷的,就是完整生存期。通常狀況下,一個活動會在 onCreate() 方法中完成各類初始化操做,而在 onDestroy() 方法種完成釋放內存的操做。
可見生存期():活動在 onStart() 方法和 onStop() 方法之間所經歷的,就是可見生存期。在可見生存期內,活動對於用戶老是可見的,即使有可能沒法和用戶進行交互。咱們能夠經過這兩個方法,合理地管理那些對用戶可見的資源。好比在 onStart() 方法中對資源進行加載,而在 onStop() 方法中對資源進行釋放,從而保證處於中止狀態的活動不會佔用過多內存。
前臺生存期:活動在 onResume() 方法和 onPause() 方法之間所經歷的,就是前臺生存期。在前臺生存期內,活動老是處於運行狀態的,此時的活動是能夠和用戶進行交互的,咱們平時看到和接觸最多的就是這個狀態下的活動。
咱們看下官方給出的 Activity 生命週期的示意圖:
咱們先定義一個:NormalActivity
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".NormalActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is a normal activity" /> </android.support.constraint.ConstraintLayout>
再定義一個:DialogActivity
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".DialogActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is a dialog activity" /> </android.support.constraint.ConstraintLayout>
爲了讓 DialogActivity 使用對話框式主題,咱們在 AndroidManifest.xml 中作以下設置:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.marco.activitylifecycletest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".NormalActivity" /> <activity android:name=".DialogActivity" android:theme="@style/Theme.AppCompat.Dialog"> // Look here </activity> </application> </manifest>
接下來修改 activity_main.xml,從新定製主活動的佈局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/start_normal_activity" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start NormalActivity" /> <Button android:id="@+id/start_dialog_activity" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start DialogActivity" /> </LinearLayout>
修改 MainActivity :
package com.example.marco.activitylifecycletest; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; private Button startNormalActivity = null; private Button startDialogActivity = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startNormalActivity = findViewById(R.id.start_normal_activity); startDialogActivity = findViewById(R.id.start_dialog_activity); startNormalActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, NormalActivity.class); startActivity(intent); } }); startDialogActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, DialogActivity.class); startActivity(intent); } }); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume"); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause"); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart"); } }
(1)當 MainActivity 第一次被建立時,以下方法被執行:
2018-10-18 04:31:29.071 2526-2526/? D/MainActivity: onCreate 2018-10-18 04:31:29.077 2526-2526/? D/MainActivity: onStart 2018-10-18 04:31:29.083 2526-2526/? D/MainActivity: onResume
(2)點擊 Start NormalActivity 按鈕:
2018-10-18 04:33:02.159 2526-2526/com.example.marco.activitylifecycletest D/MainActivity: onPause 2018-10-18 04:33:02.745 2526-2526/com.example.marco.activitylifecycletest D/MainActivity: onStop
由於 NormalActivity 已經把 MainActivity 徹底遮擋住,所以 onPause() 和 onStop() 方法都會獲得執行。
(3)點擊 Back 鍵返回 MainActivity:
2018-10-18 04:35:00.010 2526-2526/com.example.marco.activitylifecycletest D/MainActivity: onRestart 2018-10-18 04:35:00.012 2526-2526/com.example.marco.activitylifecycletest D/MainActivity: onStart 2018-10-18 04:35:00.014 2526-2526/com.example.marco.activitylifecycletest D/MainActivity: onResume
因爲以前 MainActivity 已經進入了中止狀態,因此 onRestart() 方法會獲得執行,以後又會執行 onStart() 和 onResume() 方法。注意,此時 onCreate() 方法不會執行,由於 MainActivity 並無從新建立。
(4)點擊 Start DialogActivity 按鈕:
2018-10-18 04:43:38.006 3555-3555/com.example.marco.activitylifecycletest D/MainActivity: onPause
經過 Log 能夠看到,只有 onPause() 方法獲得了執行,onStop() 方法並無執行,這是由於 DialogActivity 並無徹底遮擋住 MainActivity,此時 MainActivity 只是進入了暫停狀態,並無進入中止狀態。
(5)點擊 Back 鍵返回 MainActivity:
2018-10-18 04:50:12.222 3555-3555/com.example.marco.activitylifecycletest D/MainActivity: onResume
按下 Back 鍵返回 MainActivity 也應該只有 onResume() 方法會獲得執行。
(6)在 MainActivity 界面按下 Back 鍵退出程序:
2018-10-18 04:51:47.673 3555-3555/com.example.marco.activitylifecycletest D/MainActivity: onPause 2018-10-18 04:51:48.013 3555-3555/com.example.marco.activitylifecycletest D/MainActivity: onStop 2018-10-18 04:51:48.015 3555-3555/com.example.marco.activitylifecycletest D/MainActivity: onDestroy
依次會執行 onPause()、onStop() 和 onDestroy 方法,最終銷燬 MainActivity。
咱們在以前分析 Activity 的生命週期的時候曾經提到過:若是一個活動進入了 onStop (中止)狀態,是有可能被系統回收的!
好比咱們看如下的場景:
應用中有一個活動 A ,用戶在活動 A 的基礎上啓動了活動 B ,活動 A 就進入了中止的狀態,這個時候因爲系統內存不足,將活動 A 回收掉了,而後用戶按下 Back 鍵返回活動 A ,會出現什麼狀況呢?
其實仍是會正常顯示活動 A 的,可是此時並不會執行 onRestart() 方法了,而是會執行活動 A 的 onCreate() 方法,由於活動 A 在這種狀況下會被從新建立一次。
可能這並不會影響正常的功能,可是存在一個特殊狀況:若是活動 A 中存在臨時數據和狀態(好比 A 中有一個文本輸入框,咱們輸入了一些文字,而後啓動了 B 活動,若是 A 被 kill了,在從新回到 A 後,A 活動從新建立,那麼數據都丟失了),此時會嚴重影響用戶體驗,該怎麼辦?
其實官方文檔給出瞭解決方案,Activity 中提供了一個 onSaveInstanceState() 回調方法,這個方法能夠保證在活動被回收以前必定會被調用,所以咱們能夠經過這個方法來解決活動被回收時臨時數據得不到保存的問題。
onSaveInstanceState() 方法會攜帶一個 Bundle 類型的參數,Bundle 提供了一系列的方法用於保存數據,好比可使用 putString() 方法保存字符串,使用 putInt() 方法保存整型數據,依次類推。
每一個保存方法須要傳入兩個參數,第一個參數是鍵,用於後面從 Bundle 中取值,第二個參數是真正要保存的內容。
咱們如今對上面的代碼進行修改,在 MainActivity 中添加以下代碼將臨時數據進行保存:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); String tempData = "Something you just typed"; outState.putString("data_key", tempData); }
Ok,數據保存好了,那咱們應該在哪邊進行恢復?
不知道你有沒有發現,在 onCreate() 方法中有一個 Bundle 類型的參數。這個參數通常狀況下是 null ,可是若是在活動被系統回收以前有經過 onSaveInstanceState() 方法來保存數據的話,這個參數就會帶有以前所保存的所有數據,咱們只須要再經過相應的取值方法將數據取出便可。
修改 MainActivity 的 onCreate() 方法:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "onCreate"); if (savedInstanceState != null) { String tempData = savedInstanceState.getString("data_key"); Log.d(TAG, tempData); } ... ... }
經過上面的方法取出值以後再作相應的恢復操做就能夠了,好比說將文本內容從新賦值到文本輸入框上便可。