文章目錄
- Activity 的生命周期和啟動模式
- 1. 任務和返回棧
- 2. Activity 的四種狀態
- 2.1 運行狀態
- 2.2 暫停狀態
- 2.3 停止狀態
- 2.4 銷毀狀態
- 3. Activity的生命周期
- 3.1 生命周期回調方法
- 3.2 演示 Activity 的生命周期
- 4. Activity的啟動模式
- 4.1 standard(標準模式)
- 4.2 singleTop(棧頂復用模式)
- 4.3 singleTask(棧內復用模式)
- 4.4 singleInstance(單實例模式)
- 4.5 使用 Intent Flags
Activity 的生命周期和啟動模式
1. 任務和返回棧
《第一行代碼》原文如下:
Android 是使用任務(Task)來管理活動(Activity)的,一個任務就是一組存放在棧里的活動的集合,這個棧也被稱作返回棧(Back Stack)。棧是一種后進先出的數據結構,在默認情況下,每當我們啟動一個新的活動,他會在返回棧入棧,并處于棧頂的位置。而每當我們按下 Back 鍵或調用 finish() 方法去銷毀一個活動時,處于棧頂的活動會出棧,這時前一個入棧的活動就會重新處于棧頂的位置。系統總是會顯示處于棧頂的活動給用戶。
下圖展示了返回棧是如何管理活動入棧出棧操作的:
返回棧就像一個“頁面歷史記錄”,記錄了用戶打開的 Activity 的順序;任務是一個運行中應用組件的集合,包含一個返回棧。
2. Activity 的四種狀態
在Android開發中,活動狀態(Activity Status)指的是一個活動在其生命周期中的不同狀態。主要有以下幾種狀態:
- 運行狀態(Running):活動在屏幕前景中顯示,用戶可以與之交互。
- 暫停狀態(Paused):活動仍然可見,但失去焦點,用戶無法與之交互。
- 停止狀態(Stopped):活動完全不可見,但仍然保留其狀態和成員信息。
- 銷毀狀態(Destroyed):活動被系統銷毀,釋放資源。
這些狀態的管理對于提升應用的性能和用戶體驗至關重要。
2.1 運行狀態
當一個活動位于返回棧的棧頂時,這時活動就處于運行狀態。系統最不愿回收的就是處于運行狀態的活動,因為這會帶來非常差的用戶體驗。
2.2 暫停狀態
當一個活動不再處于棧頂位置,但仍然可見時,這時活動就進入了暫停狀態。你可能會覺得既然活動已經不在棧頂了,還怎么會可見呢?這是因為并不是每一個活動都會占滿整個屏幕的,比如對話框形式的活動只會占用屏幕中間的部分區域,你很快就會在后面看到這種活動。處于暫停狀態的活動仍然是完全存活著的,系統也不愿意去回收這種活動(因為它還是可見的,回收可見的東西都會在用戶體驗方面有不好的影響),只有在內存極低的情況下,系統才會去考慮回收這種活動。
2.3 停止狀態
當一個活動不再處于棧頂位置,并且完全不可見的時候,就進入了停止狀態。系統仍然會為這種活動保存相應的狀態和成員變量,但是這并不是完全可靠的,當其他地方需要內存時,處于停止狀態的活動有可能會被系統回收。
2.4 銷毀狀態
當一個活動從返回棧中移除后就變成了銷毀狀態。系統會最傾向于回收處于這種狀態的活動,從而保證手機的內存充足。
3. Activity的生命周期
在 Android 系統中,Activity
是用戶界面的基本構建塊,代表一個單一的屏幕。用戶與應用交互時(如打開新屏幕、返回、切換到其他應用、屏幕旋轉等),Activity 會經歷一系列狀態變化。Android 系統通過回調方法通知 Activity 這些狀態變化,開發者需要覆蓋這些方法來管理資源、保存數據、提供流暢的用戶體驗。
回調方法:當 Activity 在狀態間轉換時,系統會自動調用的方法(如
onCreate()
,onStart()
)。開發者重寫這些方法來執行特定操作。
3.1 生命周期回調方法
Activity 類中定義了7 個回調方法:
onCreate()
- 觸發時機: Activity 首次創建時調用。這是生命周期中必須實現的方法。
- 主要職責:
- 執行基本應用啟動邏輯(只應執行一次)。
- 調用
setContentView()
來設置 Activity 的布局。 - 初始化關鍵的、生命周期感知的組件(如
ViewModel
)。 - 綁定數據到 UI(如果數據已準備好)。
- 下一個狀態:
Started
(系統緊接著調用onStart()
)。
onStart()
- 觸發時機: Activity 變得可見給用戶時調用(但可能不在前臺,例如被一個非全屏對話框或另一個透明 Activity 部分覆蓋)。在
onCreate()
之后或 Activity 從Stopped
狀態恢復時調用。 - 主要職責:
- 準備讓 Activity 進入前臺并與用戶交互所需的資源(這些資源在
onStop()
中釋放)。 - 注冊需要響應 Activity 可見性的監聽器(如廣播接收器、位置更新監聽器)。
- 啟動 UI 更新所需的動畫或線程。
- 準備讓 Activity 進入前臺并與用戶交互所需的資源(這些資源在
- 下一個狀態:
Resumed
(如果 Activity 進入前臺) 或Stopped
(如果 Activity 被完全隱藏)。
onResume()
- 觸發時機: Activity 位于棧頂并獲得用戶焦點時調用。用戶正在與 Activity 交互。
- 主要職責:
- 啟動或恢復需要用戶交互的組件(如攝像頭、高精度位置更新、傳感器、動畫、獨占的音頻/視頻資源)。
- 確保 UI 是最新的(數據可能在上次暫停后發生了變化)。
- 下一個狀態:
Paused
(當 Activity 失去焦點但仍部分可見時)。
onPause()
- 觸發時機: Activity 失去焦點但仍部分可見時調用(例如,被一個非全屏對話框、透明 Activity 或系統 UI 如權限對話框部分覆蓋)。或者當 Activity 即將進入
Stopped
狀態時(系統會在onStop()
之前調用onPause()
)。 - 主要職責:
- 釋放或調整在
onResume()
中獲取的、不需要在暫停狀態下運行的獨占資源(如攝像頭、傳感器、高精度定位、獨占音頻焦點)。 - 提交未保存的更改(但輕量級的,因為系統可能很快調用
onResume()
)。不建議執行 CPU 密集型操作或持久化數據(如數據庫寫入),這會延遲到下一個 Activity 的轉換。 - 為進入
Stopped
狀態做準備。
- 釋放或調整在
- 下一個狀態:
Resumed
(如果用戶返回到該 Activity) 或Stopped
(如果 Activity 被完全隱藏)。
- 觸發時機: Activity 失去焦點但仍部分可見時調用(例如,被一個非全屏對話框、透明 Activity 或系統 UI 如權限對話框部分覆蓋)。或者當 Activity 即將進入
onStop()
- 觸發時機: Activity 對用戶完全不可見時調用(例如,被另一個 Activity 完全覆蓋、用戶切換到其他應用、或 Activity 本身即將被銷毀)。
- 主要職責:
- 釋放或調整所有在
onStart()
中獲取的、不需要在不可見狀態下運行的資源(如廣播接收器、位置更新監聽器、UI 動畫線程)。 - 執行 CPU 相對密集的關閉操作(如將數據保存到數據庫)。
- 保存當前的 UI 狀態(通過
onSaveInstanceState()
)。
- 釋放或調整所有在
- 下一個狀態:
Restarted
(如果 Activity 重新變得可見) 或Destroyed
(如果系統需要回收內存或用戶顯式關閉 Activity)。
onDestroy()
- 觸發時機: Activity 即將被銷毀或已完成銷毀時調用。這可能發生在:
- 用戶顯式關閉 Activity(如按返回鍵)。
- 在 Activity 處于
Stopped
狀態且系統需要回收內存時被系統終止。 - 由于配置變更(如屏幕旋轉、語言更改)系統臨時銷毀該 Activity 實例(緊接著會創建一個新的實例)。
- 主要職責:
- 執行最終的清理工作(如注銷全局監聽器、釋放可能泄漏的資源)。
- 確保所有在
onCreate()
中創建并長期運行的操作(如網絡請求)被取消或清理。 - 區分是永久銷毀還是配置變更導致的臨時銷毀(檢查
isFinishing()
)。
onRestart()
- 在 Activity 從
Stopped
狀態重新回到Started
狀態之前調用(即在onStart()
之前)。 - 用于執行僅在 Activity 從停止狀態恢復時才需要的特殊初始化(不同于首次創建的初始化)。
- 在 Activity 從
下圖是 Android 官方提供的一張活動生命周期示意圖:
這七種方法除了 onRestart() 方法,其他都是兩兩相對的,從而又可將活動分為 3 種生存期:
- 完整生存期
- 范圍:
onCreate()
?onDestroy()
- 核心特點:
- 覆蓋 Activity 從創建到銷毀的完整過程
onCreate()
完成全局初始化(布局、數據綁定等)onDestroy()
執行最終清理(釋放內存、關閉連接等)
- 范圍:
- 可見生存期
- 范圍:
onStart()
?onStop()
- 核心特點:
- Activity 對用戶可見的整個階段
- 包括前臺(可交互)和后臺(被部分遮擋)狀態
- 資源管理原則:可見時加載,不可見時釋放
- 范圍:
- 前臺生存期
- 范圍:
onResume()
?onPause()
- 核心特點:
- Activity 處于棧頂且可交互的狀態
- 用戶直接操作的黃金時期
- 響應速度要求最高(禁止耗時操作)
- 范圍:
《第一行代碼》原文:
Activity 類中定義了 7 個回調方法,覆蓋了活動生命周期的每一個環節,喜愛按就來一一介紹這 7 個方法。
onCreate()
它會在活動第一次創建的時候調用。應該在這個方法中完成活動的初始化操作,比如加載布局、綁定事件等。onStart()
這個方法在活動由不可見變為可見的時候調用。onResume()
這個方法在活動準備好和用戶進行交互的時候調用。此時的活動一定位于返回棧的棧頂,并且處于運行狀態。onPause()
這個方法在系統準備去啟動或者恢復另一個活動的時候調用。通常會在這個方法中將一些消耗CPU的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂活動的使用。onDestory()
這個方法在活動被銷毀之前調用,之后活動的狀態將變為銷毀狀態。onRestart()
這個方法在活動由停止狀態變為運行狀態之前調用,也就是活動被重新啟動了。以上 7 個方法除了 onRestart() 方法,其他都是兩兩相對的,從而又可將活動分為 3 種生存期。
完整生存期
活動在 onCreate() 方法和 onDestroy() 方法之間所經歷的就是完整生存期。一般情況下,一個活動會在 onCreate() 方法中完成各種初始化操作,而在 onDestroy() 方法中完成釋放內存的操作。
可見生存期
活動在 onStart() 方法和 onStop() 方法之間所經歷的就是可見生存期。 在可見生存期內,活動對于用戶總是可見的,即便有可能無法和用戶進行交互。我們可以通過這兩個方法,合理地管理那些對用戶可見的資源。比如在 onStart() 方法中對資源進行加載,而在 onStop() 方法中對資源進行釋放,從而保證處于停止狀態的活動不會占用過多內存。
前臺生存期
活動在 onResume() 方法和 onPause() 方法之間所經歷的就是前臺生存期。 在前臺生存期內,活動總是處于運行狀態的,此時的活動是可以和用戶進行交互的,我們平時看到和接觸最多的也就是這個狀態下的活動。
Activity 生命周期回調方法總結
回調方法 | 觸發時機 | 核心用途 | 下一狀態 |
---|---|---|---|
onCreate() | Activity 首次創建時調用 | 1. 初始化基本組件 2. 調用 setContentView() 綁定布局 3. 初始化數據(如 ViewModel) | onStart() |
onStart() | Activity 對用戶可見時 | 1. 注冊與可見性相關的監聽器 2. 準備前臺顯示所需資源 | onResume() 或 onStop() |
onResume() | Activity 進入前臺并獲得焦點 | 1. 啟動用戶交互組件(如動畫、傳感器) 2. 刷新 UI 數據 | onPause() |
onPause() | Activity 失去焦點但仍部分可見 | 1. 釋放獨占資源(如相機) 2. 提交輕量級數據保存 | onResume() 或 onStop() |
onStop() | Activity 完全不可見時 | 1. 釋放所有可見性相關資源 2. 保存重要數據到持久存儲 3. 調用 onSaveInstanceState() | onRestart() 或 onDestroy() |
onRestart() | Activity 從停止狀態重新顯示前 | 執行恢復可見時的特殊初始化 | onStart() |
onDestroy() | Activity 銷毀前(永久或臨時) | 1. 清理所有資源 2. 終止后臺任務 3. 區分 isFinishing() | 無 |
注:以上所有方法均可通過重寫(
@Override
)在自定義 Activity 中實現。
3.2 演示 Activity 的生命周期
以下是一個簡單的 Android 活動生命周期演示代碼,通過日志輸出清晰展示各個生命周期方法的觸發時機:
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {// 日志標簽private static final String TAG = "LifecycleDemo";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "onCreate - 活動被創建");// 添加關閉按鈕點擊事件findViewById(R.id.close_button).setOnClickListener(v -> {Log.d(TAG, "用戶點擊關閉按鈕");finish(); // 結束活動});}@Overrideprotected void onStart() {super.onStart();Log.d(TAG, "onStart - 活動可見");}@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "onResume - 活動可交互");}@Overrideprotected void onPause() {super.onPause();Log.d(TAG, "onPause - 活動失去焦點");}@Overrideprotected void onStop() {super.onStop();Log.d(TAG, "onStop - 活動不可見");}@Overrideprotected void onRestart() {super.onRestart();Log.d(TAG, "onRestart - 活動重新啟動");}@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy - 活動被銷毀");}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"android:padding="16dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="活動生命周期演示"android:textSize="20sp"android:textStyle="bold"/><TextViewandroid:id="@+id/lifecycle_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="查看Logcat中的生命周期日志"android:textSize="18sp"android:layout_marginTop="24dp"/><Buttonandroid:id="@+id/close_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="關閉活動"android:layout_marginTop="24dp"/></LinearLayout>
初始啟動應用
按下返回鍵
點擊“關閉活動”按鈕
按下 Home 鍵返回桌面
從最近任務重新打開應用
旋轉屏幕(測試配置變更)
4. Activity的啟動模式
Android 的活動啟動模式決定了活動實例如何與任務棧交互,是 Android 系統管理活動實例的重要機制,在實際項目中我們應根據特定的需求為每個活動指定恰當的啟動模式。啟動模式一共有四種:standard、singleTop、singleTask 和 singleInstance,可以在 AndroidManifest.xml 中通過給<activity>
標簽指定 android:launchMode
屬性來選擇啟動模式。
示例代碼:
<activityandroid:name=".MainActivity"android:exported="true"android:launchMode="singleTop">
4.1 standard(標準模式)
《第一行代碼》原文:
standard 是活動默認的啟動模式,在不進行顯式指定的情況下,所有活動都會自動使用這種啟動模式。因此,到目前為止我們寫過的所有活動都是使用的standard模式。經過上一節的學習,你已經知道了 Android 是使用返回棧來管理活動的,在standard模式(即默認情況)下,每當啟動一個新的活動,它就會在返回棧中入棧,并處于棧頂的位置。對于使用 standard 模式的活動,系統不會在乎這個活動是否已經在返回棧中存在,每次啟動都會創建該活動的一個新的實例。
- 默認模式,每次啟動活動都會創建新實例。
- 新實例被壓入啟動它的任務棧棧頂
- 適用場景:普通活動,需要多個實例的情況
4.2 singleTop(棧頂復用模式)
《第一行代碼》原文:
可能在有些情況下,你會覺得 standard 模式不太合理。活動明明已經在棧頂了,為什么再次啟動的時候還要創建一個新的活動實例呢?別著急,這只是系統默認的一種啟動模式而已,你完全可以根據自己的需要進行修改,比如說使用 singleTop 模式。當活動的啟動模式指定為 singleTop,在啟動活動時如果發現返回棧的棧頂已經是該活動,則認為可以直接使用它,不會再創建新的活動實例。
- 如果目標活動已在棧頂,則復用實例(調用
onNewIntent()
) - 不在棧頂則創建新實例
- 適用場景:防止重復創建棧頂活動(如通知跳轉)
4.3 singleTask(棧內復用模式)
《第一行代碼》原文:
使用 single Top 模式可以很好地解決重復創建棧頂活動的問題,但是正如你在上一節所看到的,如果該活動并沒有處于棧頂的位置,還是可能會創建多個活動實例的。那么有沒有什么辦法可以讓某個活動在整個應用程序的上下文中只存在一個實例呢?這就要借助 single Task 模式來實現了。當活動的啟動模式指定為singleTask,每次啟動該活動時系統首先會在返回棧中檢查是否存在該活動的實例,如果發現已經存在則直接使用該實例,并把在這個活動之上的所有活動統統出棧,如果沒有發現就會創建一個新的活動實例。
- 系統會尋找匹配的任務棧(由
taskAffinity
決定) - 如果存在實例,則清除其上的所有活動并調用
onNewIntent()
- 不存在則創建新實例
- 適用場景:應用入口活動(如主界面)
4.4 singleInstance(單實例模式)
《第一行代碼》原文:
singleInstance 模式應該算是 4 種啟動模式中最特殊也最復雜的一個了,你也需要多花點功夫來理解這個模式。不同于以上 3 種啟動模式,指定為 singleInstance 模式的活動會啟用一個新的返回棧來管理這個活動(其實如果singleTask 模式指定了不同的taskAffinity,也會啟動一個新的返回棧)。那么這樣做有什么意義呢?想象以下場景,假設我們的程序中有一個活動是允許其他程序調用的,如果我們想實現其他程序和我們的程序可以共享這個活動的實例,應該如何實現呢?使用前面 3 種啟動模式肯定是做不到的,因為每個應用程序都會有自己的返回棧,同一個活動在不同的返回棧中入棧時必然是創建了新的實例。而使用 singleInstance 模式就可以解決這個問題,在這種模式下會有一個單獨的返回棧來管理這個活動,不管是哪個應用程序來訪問這個活動,都共用的同一個返回棧,也就解決了共享活動實例的問題。
- 活動獨占一個任務棧
- 全局唯一實例
- 后續啟動直接復用該實例
- 適用場景:需要全局共享的活動(如來電界面)
taskAffinity
: 這個屬性通常與singleTask
和singleInstance
結合使用,用于指定Activity
希望歸屬的任務棧的名稱(默認為應用包名)。它影響singleTask
尋找或創建哪個專屬棧,以及Intent
標志FLAG_ACTIVITY_NEW_TASK
的行為。onNewIntent()
: 對于復用實例的模式 (singleTop
,singleTask
,singleInstance
),當復用發生時,新的Intent
是通過onNewIntent()
方法傳遞的,而不是onCreate()
。務必在此方法中處理新的意圖數據。
4.5 使用 Intent Flags
啟動模式也可以動態指定,優先級高于清單中聲明:
Intent intent = new Intent(this, TargetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
常用標志:
標志 | 說明 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 在新任務中啟動 Activity |
FLAG_ACTIVITY_SINGLE_TOP | 等同于 singleTop |
FLAG_ACTIVITY_CLEAR_TOP | 清除目標 Activity 之上的所有 Activity |
FLAG_ACTIVITY_CLEAR_TASK | 在啟動前清除整個任務的所有 Activity |
FLAG_ACTIVITY_REORDER_TO_FRONT | 如果 Activity 已存在,將其移到棧頂 |