搞懂活動的生命週期很重要,搞懂之後你才能寫出流暢的程式
而且在合理管理應用程式資源方面也能發揮得更好。
帶來更好的使用者體驗
Android 的活動是可以層疊的
每次啟動一個新活動,它就會覆蓋在原本的活動上面。
然後點擊 back 鍵可以銷毀目前的活動,上一次的活動就會重新出現。
其實 Android 是使用任務(Task)來管理的。
一個任務就是一組存放在堆疊(stack)裡的活動集合。
這個堆疊被稱為返回堆疊(Back Stack)。
堆疊是一種後進先出(LIFO)的資料結構。
預設情況下,每當啟動一個新活動,它就會被推入堆疊並放在最上面。
當我們按下返回鍵或執行了 finish(),處於堆疊頂端的活動就會被銷毀。然後前一個入堆疊的活動就會回到頂端。系統永遠會把堆疊頂端的活動顯示給使用者。
活動狀態
每個活動在生命週期中最多可能會有四種狀態。
1. 執行中狀態(Running):
處於堆疊頂端時,系統不願意回收它,否則會嚴重影響使用者體驗。
2. 暫停狀態(Paused)
不在堆疊頂端,但仍然可見。例如目前活動彈出一個對話框活動,對話框只占螢幕一部分,類似模態視窗。系統也不願意回收這種,因為突然消失會讓使用者覺得很奇怪。
3. 停止狀態(Stopped)
當一個活動不在堆疊頂端且完全不可見時,就進入停止狀態。系統仍會為這種活動保留狀態和成員變數,但這並不可靠——如果其他地方需要記憶體,系統也會考慮回收這類活動。
4. 銷毀狀態(Destroyed)
當一個活動從返回堆疊中被移除後,就變成銷毀狀態。系統最優先回收這種活動,以確保手機有充足的記憶體。
活動的生存期與七個回呼方法
Activity 類別定義了七個回呼方法,涵蓋了活動生命週期的每一個環節。
簡單來說,這七個回呼函式會在生命週期的特定時機被觸發,然後執行裡面的程式碼。
例如活動第一次建立時會觸發 onCreate。
以下介紹這七個方法。

onCreate:
活動第一次建立時呼叫,可以在這裡完成活動的初始化,例如載入佈局、綁定按鈕事件等。
onStart:
當活動從不可見變為可見時觸發。
onResume:
這個方法在活動準備好與使用者互動時呼叫。此時活動處於返回堆疊的頂端,並且處於執行中狀態。(活動已經被使用者看到了)
onPause:
系統準備啟動或恢復另一個活動時呼叫。通常會在這個方法中釋放一些耗 CPU 的資源,並儲存關鍵資料,但這個方法的執行速度一定要快,否則會影響新活動的顯示。(目前活動即將被隱藏,要進入下一個活動但還沒完全進入)
onStop:
在活動完全不可見時呼叫。它和 onPause 的主要差別在於:如果啟動的新活動是對話框活動,則 onPause 會執行,但 onStop 不會。(活動被隱藏,然後新活動出現)
onDestroy:
活動被銷毀之前呼叫,之後活動的狀態變成銷毀狀態。
onRestart:
活動從停止狀態變為執行中狀態之前呼叫,也就是活動被重新啟動時。
三種生存期
上面七個方法,除了 onRestart 之外,其他都是兩兩相對,所以可以把活動劃分為三種生存期。
1. 完整生存期
活動從 onCreate 到 onDestroy 方法之間所經歷的,就是完整生存期。
2. 可見生存期:
從 onStart 到 onStop 之間所經歷的,就是可見生存期。活動對使用者始終是可見的。可以透過這兩個方法合理管理資源,例如在 onStart 載入資源,在 onStop 釋放資源,確保處於停止狀態的活動不會佔用過多資源。
3. 前景生存期
活動從 onResume 到 onPause 之間所經歷的稱為前景生存期。活動始終處於執行中狀態,此時活動可以與使用者互動。我們平常看到和接觸最多的就是這種狀態下的活動。
開啟主活動 → 進入普通活動 → 返回主活動 → 回到桌面
05-12 15:32:43.899 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onCreate:
05-12 15:32:43.926 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onStart:
05-12 15:32:43.930 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onResume:
05-12 15:33:00.973 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onPause:
05-12 15:33:01.078 4316-4316/com.foxdevelop.www.lifedemo E/普通活動日誌: onCreate:
05-12 15:33:01.079 4316-4316/com.foxdevelop.www.lifedemo E/普通活動日誌: onStart:
05-12 15:33:01.081 4316-4316/com.foxdevelop.www.lifedemo E/普通活動日誌: onResume:
05-12 15:33:02.969 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onStop:
05-12 15:34:01.747 4316-4316/com.foxdevelop.www.lifedemo E/普通活動日誌: onPause:
05-12 15:34:01.779 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onRestart:
05-12 15:34:01.779 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onStart:
05-12 15:34:01.780 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onResume:
05-12 15:34:02.749 4316-4316/com.foxdevelop.www.lifedemo E/普通活動日誌: onStop:
05-12 15:34:02.750 4316-4316/com.foxdevelop.www.lifedemo E/普通活動日誌: onDestroy:
05-12 15:35:26.827 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onPause:
05-12 15:35:28.534 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onStop:
05-12 15:34:01.779 4316-4316/com.foxdevelop.www.lifedemo E/主活動日誌: onDestroy:
專案實作
我們建立一個新專案來親身體驗生命週期
活動名稱我們就叫 LifeDemo。
新增一個 EmptyActivity 作為主活動。
再新增兩個普通活動 NormalActivity 和 DialogActivity。
然後修改它們的佈局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通活動" />
</LinearLayout>Code language: HTML, XML (xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="對話框活動" />
</LinearLayout>Code language: HTML, XML (xml)
它們的佈局除了顯示的文字不同,其他基本上一樣。
我們還需要把 DialogActivity 改成對話框樣式的活動。
在 AndroidManifest.xml 中修改:
<activity android:name=".NormalActivity" />
<activity android:name=".DialogActivity" android:theme="@style/Base.Theme.AppCompat.Dialog"/>
</application>Code language: HTML, XML (xml)
這樣就把 DialogActivity 設為對話框主題了。
主活動佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="開啟普通活動" />
<Button
android:id="@+id/buttondialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="開啟對話框活動" />
</LinearLayout>Code language: HTML, XML (xml)
給主活動的兩個按鈕加上事件
package com.foxdevelop.www.lifedemo;
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 {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b = (Button)findViewById(R.id.button);
b.setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view){
Intent it = new Intent(MainActivity.this,NormalActivity.class);
startActivity(it);
}
}
);
Button b2 = (Button)findViewById(R.id.buttondialog);
b2.setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view){
Intent it = new Intent(MainActivity.this,DialogActivity.class);
startActivity(it);
}
}
);
}
@Override
protected void onStart() {
super.onStart();
Log.e(TAG, "onStart: ");
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume: ");
}
@Override
protected void onPause() {
super.onPause();
Log.e(TAG, "onPause: ");
}
@Override
protected void onStop() {
super.onStop();
Log.e(TAG, "onStop: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
}
@Override
protected void onRestart() {
super.onRestart();
Log.e(TAG, "onRestart: ");
}
}Code language: JavaScript (javascript)
1. 執行程式
主活動建立時會依序執行 onCreate、onStart、onResume。

點擊「開啟普通活動」按鈕,此時會觸發 onPause 和 onStop。

因為 NormalActivity 把 MainActivity 整個擋住了,所以這兩個方法都會執行。
點擊 Back 鍵返回。

因為之前的 MainActivity 已經進入停止狀態,所以會觸發 onRestart,之後再依序執行 onStart 和 onResume。
但 onCreate 方法不會執行,因為它並沒有被重新建立。其實這時候 NormalActivity 應該是觸發了它自己的 onDestroy。
再點擊「開啟對話框」按鈕。
這個時候,只觸發了 onPause。
可以看到,並沒有觸發 onStop。
那是因為 DialogActivity 並沒有把 MainActivity 完全擋住。

同樣地,此時按下 back 鍵,也只有 onResume 被執行。因為它沒有被 stop,所以不會觸發 onStart。


我們可以看出,這個時候會觸發 onPause、onStop,然後銷毀 onDestroy。
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.Code language: CSS (css)
主活動建立 → 對話框活動開啟 → 返回主活動 → 回到桌面
05-13 02:10:51.635 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onCreate: // 建立新活動
05-13 02:10:51.673 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onStart: // 從不可見變為可見
05-13 02:10:51.701 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onResume: // 活動可見了
05-13 02:12:01.993 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onPause: // 從目前活動要前往其他活動
05-13 02:12:02.115 4206-4206/com.foxdevelop.www.lifedemo E/對話框活動: onCreate:
05-13 02:12:02.116 4206-4206/com.foxdevelop.www.lifedemo E/對話框活動: onStart:
05-13 02:12:02.143 4206-4206/com.foxdevelop.www.lifedemo E/對話框活動: onResume:
05-13 02:14:26.078 4206-4206/com.foxdevelop.www.lifedemo E/對話框活動: onPause: // 按 back 鍵,要返回主活動
05-13 02:14:26.367 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onResume: // 主活動直接出現,因為沒有 stop,所以沒有 restart 和 start
05-13 02:14:26.803 4206-4206/com.foxdevelop.www.lifedemo E/對話框活動: onStop: // 對話框活動不可見了
05-13 02:14:26.803 4206-4206/com.foxdevelop.www.lifedemo E/對話框活動: onDestroy: // 因為是 back 或 finish,所以它要被銷毀
05-13 02:16:23.959 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onPause:
05-13 02:16:25.606 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onStop:
05-13 02:16:25.609 4206-4206/com.foxdevelop.www.lifedemo E/主活動: onDestroy: // 任何銷毀之前,都要先 pause 再 stopCode language: JavaScript (javascript)
活動的生命週期