Activity生命周期和四大啟動模式詳解
一、Activity 生命周期
Activity 的生命周期由一系列回調方法組成,用于管理其創建、可見性、焦點和銷毀過程。以下是核心方法及其調用時機:
-
?onCreate()??
- ?調用時機?:Activity 首次創建時調用。
- ?作用?:初始化布局(
setContentView
)、綁定數據、創建后臺線程等。 - ?注意?:在此方法中應避免耗時操作。
-
?onStart()??
- ?調用時機?:Activity 可見但未獲得焦點(例如被對話框覆蓋)。
- ?作用?:恢復UI更新或資源加載。
-
?onResume()??
- ?調用時機?:Activity 進入前臺并可與用戶交互。
- ?作用?:啟動動畫、傳感器監聽、高頻率更新UI等。
- ?關鍵點?:此時 Activity 位于棧頂。
-
?onPause()??
- ?調用時機?:Activity 失去焦點(如彈出對話框或跳轉到其他 Activity)。
- ?作用?:保存臨時數據、釋放資源(如攝像頭)。
- ?注意?:需快速執行,否則會影響新 Activity 的啟動。
-
?onStop()??
- ?調用時機?:Activity 完全不可見(被其他 Activity 覆蓋或退出)。
- ?作用?:停止動畫、釋放非必要資源。
-
?onDestroy()??
- ?調用時機?:Activity 被銷毀(用戶主動退出或系統回收資源)。
- ?作用?:清理內存、注銷廣播等。
-
?onRestart()??
- ?調用時機?:Activity 從停止狀態重新回到前臺(如按返回鍵返回)。
- ?流程?:
onRestart()
→onStart()
→onResume()
。
場景應用
場景 1:打開新頁面?
?流程?:
- 原 Activity 執行?
onPause()
(失去焦點) - 新 Activity 依次執行:
onCreate()
(初始化)onStart()
(可見)onResume()
(可交互)
- 原 Activity 執行?
onStop()
(完全不可見)
?典型場景?:
- 從主頁跳轉到詳情頁
- 列表頁打開新的商品頁
?場景 2:返回上一個頁面?
?流程?:
- 當前 Activity 執行?
onPause()
- 上一個 Activity 依次執行:
onRestart()
(重新激活)onStart()
(可見)onResume()
(可交互)
- 當前 Activity 執行:
onStop()
onDestroy()
(被銷毀)
?典型場景?:
- 提交訂單后返回購物車
- 查看圖片詳情后返回相冊
?場景 3:屏幕旋轉?
?流程?:
onPause()
?→?onSaveInstanceState()
(保存數據)onStop()
?→?onDestroy()
(銷毀實例)- 重新創建:
onCreate()
(攜帶保存的數據)onStart()
onRestoreInstanceState()
(恢復數據)onResume()
?開發重點?:
- 在?
onSaveInstanceState()
?保存編輯框內容/滾動位置 - 避免在旋轉時中斷網絡請求
?場景 4:被彈窗/來電中斷?
?場景? | ?生命周期變化? | ?恢復順序? |
---|---|---|
普通對話框 | onPause() | onResume() |
全屏彈窗/來電 | onPause() ?→?onStop() | onRestart() ?→?onResume() |
?關鍵區別?:
- 是否完全遮擋決定是否觸發?
onStop()
二、四大啟動模式(Launch Mode)
????????Android 的四大啟動模式核心基于任務棧管理機制實現。系統通過 ActivityManagerService(AMS)維護一個全局的任務棧結構,每個任務棧由 TaskRecord 對象表示,遵循后進先出的堆棧原則。當啟動 Activity 時,AMS 會根據不同啟動模式的預設規則,動態調整任務棧的組成。
?????????standard 模式是最基礎的方式。它簡單地無條件創建新 Activity 實例,并將其壓入當前任務棧頂部。這種模式不涉及任何復用判斷,因此可能在棧中積累多個相同 Activity 實例,對內存管理構成壓力。系統在處理時直接調用 ActivityStarter 的強制壓棧方法,整個過程沒有任何條件檢測。
適用場景?:
- ?常規頁面跳轉?(如商品詳情頁、新聞內容頁)
- ?多實例并行需求?(如瀏覽器標簽頁、文檔編輯頁)
?典型案例?: - 電商應用瀏覽商品:
主頁 → 商品A頁 → 商品B頁
,棧內存在多個不同商品頁實例 - 文檔應用編輯文件:同時打開
文檔1
、文檔2
,返回時可逐個切換
?????????singleTop 模式的核心在于棧頂檢查機制。啟動時,AMS 會立即判斷目標 Activity 是否恰好位于當前棧頂。如果匹配成功,則直接觸發該實例的 onNewIntent() 進行數據刷新,完全跳過創建流程;若不在棧頂,則回退到 standard 模式創建新實例。這種設計特別適合通知欄點擊等高頻重復啟動的場景。
適用場景?:
- ?避免重復通知?(通知欄多次點擊跳轉同一頁)
- ?高頻操作入口?(如刷新按鈕、重復提交頁面)
?典型案例?: - 微信消息頁面:當停留在
聊天頁A
時收到新消息,點擊通知直接刷新當前頁面 - 支付倒計時頁:用戶多次點擊“重新支付”按鈕,復用當前頁面更新倒計時
?特殊場景?: - 掃二維碼頁面:已打開掃碼頁時再次觸發掃碼,直接復用而非新建
?????????singleTask 模式采用任務棧重構策略。系統會優先搜索與目標 Activity 的 taskAffinity 匹配的任務棧,檢測是否存在已有實例。如果找到,AMS 會先清除該實例之上的所有 Activity(觸發它們的 onDestroy),將目標實例置于棧頂并調用 onNewIntent();若未找到,則新建獨立任務棧。這種清除機制有效保障了棧內實例唯一性,但處理成本較高。
適用場景?:
- ?應用主頁?(如微信主界面、設置首頁)
- ?登錄/授權中轉站?(確保返回時跳過登錄頁)
?典型案例?: - 支付流程結束:
商品頁 → 收銀臺 → 支付成功頁
,點擊“返回首頁”清空整個支付棧,直達主頁 - 社交應用登錄:若已登錄過,再次打開APP直接進入
主頁面
而非登錄頁
?????????singleInstance 模式具有全局隔離特性。該模式強制目標 Activity 獨占整個任務棧,且棧內不允許其他 Activity 存在。啟動時,AMS 先全局搜索專屬棧:若存在則直接提至前臺復用;若不存在則創建帶唯一 ID 的新棧。這種模式實現了跨應用級的隔離,在系統低內存回收時可能被獨立保留,常用于相機、撥號等系統組件。
適用場景?:
- ?系統級共享組件?(相機、通訊錄選擇器)
- ?獨立功能模塊?(如浮窗通話界面)
?典型案例?: - 系統相機調用:應用中啟動相機拍照,返回時直接回到原應用
- 第三方分享面板:微信分享時調起系統分享棧,完成后自動銷毀
?特殊注意?: - 銀行應用安全鍵盤:通過獨立進程防止密碼輸入被截屏
場景需求? | ?推薦模式? |
---|---|
常規頁面跳轉(允許多實例) | standard |
避免棧頂重復(如通知點擊) | singleTop |
應用入口/核心頁(需清棧) | singleTask |
獨立系統組件(跨進程調用) | singleInstance |
????????通過 AndroidManifest.xml
或 Intent 標志(如 FLAG_ACTIVITY_NEW_TASK
)指定,控制 Activity 實例與任務棧(Task)的關系。
????????onNewIntent()
?是 Activity 被復用時的數據刷新入口,用于處理同一個 Activity 實例被再次啟動時的新意圖(Intent),而無需重建頁面。
?核心作用?(3個關鍵點):
-
?復用已有頁面?
- 避免重復創建相同 Activity(節省內存)
- 保持當前頁面狀態(如滾動位置、輸入內容)
-
?更新數據?
protected void onNewIntent(Intent intent) {setIntent(intent); // 必須更新!否則getIntent()拿舊數據refreshUI(intent); // 根據新數據刷新界面 }
-
?特定場景觸發?
- ?singleTop?:當 Activity 位于棧頂時
- ?singleTask/singleInstance?:當 Activity 已存在于棧中時
?典型場景?:
- 點擊通知欄多次打開同一聊天頁(直接刷新消息)
- 從支付結果頁返回后再次支付(復用頁面更新訂單)
- 全局搜索框重復搜索(保留搜索歷史記錄)
?記住一個原則?:
只要看到?singleTop
/singleTask
,就要考慮是否需要重寫?onNewIntent()
?處理數據刷新!
啟動模式在后臺運行時的關鍵行為解析
一、后臺啟動 Activity 的通用規則
-
?基本行為?:
- 當應用在后臺時啟動新 Activity,系統會先將應用帶回前臺
- 新 Activity 會被添加到當前任務棧中
- 用戶按返回鍵時會回到上一個 Activity
-
?核心影響?:
- 應用進程優先級提升到前臺進程
- 可能觸發系統回收機制(低內存時后臺進程優先被殺)
二、后臺啟動的注意事項
1. Android 8.0+ 限制
- ?后臺啟動限制?:應用在后臺時無法隨意啟動 Activity
- ?解決方案?:
// 必須使用全屏通知 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID).setContentIntent(pendingIntent) // 用戶點擊才啟動.setFullScreenIntent(pendingIntent, true); // 緊急通知
2. 內存回收策略
啟動模式 | 回收優先級 | 恢復難度 |
---|---|---|
standard | 高 | 難(多實例) |
singleTop | 中 | 中等 |
singleTask | 低 | 易(單實例) |
singleInstance | 最低 | 最易 |
3. 最佳實踐
-
?后臺啟動 singleTask 主頁?:
// 清理所有歷史棧 Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
?避免后臺 standard 啟動?:
- 易導致 OOM(內存溢出)
- 返回棧混亂
-
?跨進程通信?:
// 啟動獨立進程的Activity <activity android:process=":camera_process"/>
三、各模式后臺表現對比表
啟動模式 | 后臺啟動特點 | 內存效率 | 適用場景 |
---|---|---|---|
standard | 持續堆疊新實例 | 低 | 需多實例的普通頁面 |
singleTop | 棧頂復用省資源 | 中 | 通知/消息更新 |
singleTask | 清理棧內多余頁面 | 高 | 應用主頁/核心入口 |
singleInstance | 獨立進程不受主應用影響 | 最高 | 相機/電話等系統級功能 |
模式 | 實例數量 | 棧位置 | 典型場景 |
---|---|---|---|
standard | 多個 | 當前棧 | 普通頁面 |
singleTop | 棧頂唯一 | 當前棧 | 防重復啟動(通知欄) |
singleTask | 棧內唯一 | 可指定新棧 | 應用主界面 |
singleInstance | 全局唯一 | 獨占新棧 | 獨立功能(如相機) |
微信小游戲雙圖標背后的啟動模式解析
場景重現:
-
點擊微信圖標啟動微信主界面
-
在微信內點擊進入小游戲
-
?后臺出現兩個獨立圖標?:
-
微信主應用圖標
-
小游戲獨立圖標
-
核心啟動模式:singleInstance
?實現原理?:
<!-- 小游戲Activity聲明示例 -->
<activityandroid:name=".GameActivity"android:launchMode="singleInstance"android:taskAffinity="com.tencent.game"android:process=":game_process" />
?三大關鍵機制?:
-
?獨立進程?
android:process=":game_process"
-
小游戲運行在獨立的
com.tencent.mm:game_process
進程 -
與微信主進程
com.tencent.mm
完全隔離 -
?效果?:系統顯示兩個獨立進程圖標
-
-
?獨立任務棧?
android:taskAffinity="com.tencent.game" android:launchMode="singleInstance"
-
創建專屬任務棧(與微信主棧隔離)
-
?效果?:
-
最近任務顯示兩個獨立任務項
-
游戲退出時直接回到手機桌面,不經過微信
-
-
-
?跨進程通信?
// 微信啟動游戲的關鍵代碼 Intent intent = new Intent(); intent.setComponent(new ComponentName("com.tencent.mm", "com.tencent.mm.plugin.game.GameActivity")); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); startActivity(intent);
-
使用
FLAG_ACTIVITY_MULTIPLE_TASK
允許多實例
-
完整啟動流程:
-
?用戶點擊小游戲入口?
-
?微信創建子進程?:
-
通過
startActivity()
跨進程啟動 -
指定
singleInstance
+NEW_TASK
標志
-
-
?系統創建資源隔離區?:
-
獨立內存空間(分配專屬內存)
-
獨立渲染線程(避免微信主線程卡頓)
-
獨立任務棧(系統記錄為獨立應用)
-
-
?游戲結束后?:
-
游戲進程銷毀
-
微信主進程不受影響
-
?返回路徑?:游戲 → 桌面 (不返回微信)
-
技術優勢:
-
?性能隔離?:
-
游戲占500MB內存不影響微信聊天
-
游戲崩潰不會導致微信閃退
# 進程內存占用示例 com.tencent.mm: 300MB # 微信主進程 com.tencent.mm:game: 500MB # 游戲進程
-
-
?獨立生命周期?:
操作
微信主進程
游戲進程
進入游戲
onPause()
onCreate()
游戲切后臺
-
onPause->onStop
關閉游戲
onResume()
onDestroy()
游戲崩潰
無影響
自動重啟
-
?用戶體驗優化?:
-
游戲可獨立操作(微信后臺保持運行)
-
小窗口模式雙向互動(微信浮窗+游戲)
-
對比其他啟動模式:
啟動模式 | 是否分進程 | 是否獨立圖標 | 適用場景 |
---|---|---|---|
standard | ? | ? | 普通頁面跳轉 |
singleTask | ? | ? | 微信錢包 |
singleTop | ? | ? | 公眾號文章 |
?singleInstance? | ? | ?**?**? | 小游戲/視頻通話 |
典型應用場景:
-
微信/QQ內置小游戲
-
直播平臺連麥功能
-
銀行App的安全鍵盤
-
AR掃描模塊
ContentProvider應用場景
?????????ContentProvider 是 Android 實現應用間安全數據共享(基于 URI 和 Binder)的核心組件,同時通過空實現 Provider 的?onCreate()
?機制實現庫的自動初始化。?
ContentProvider 的主要應用場景?
?a) 應用間共享數據(最常見目的)??
* **系統功能:** Android系統自身提供了大量ContentProvider讓應用訪問系統數據:* `ContactsContract.Contacts` (通訊錄聯系人)* `MediaStore.Images.Media` (相冊圖片)* `MediaStore.Audio.Media` (音頻文件)* `CalendarContract.Events` (日歷事件)* `CallLog.Calls` (通話記錄)* `Settings.System` (系統設置)
* **第三方應用:** 當你的應用需要向其他應用**提供數據**時(如提供用戶配置、自定義數據庫內容),你需要實現自己的ContentProvider,并定義相應的authority和URI結構。
??b) 同一個應用內的結構化數據訪問?
* 即使只在應用內部使用,ContentProvider也提供了一種**標準化**的方式來訪問應用本身的數據庫或結構化文件。這有利于統一數據訪問邏輯、解耦UI和數據層(符合MVC/MVP/MVVM架構思想)、并便于以后擴展成跨應用共享(僅需添加權限和可能的URI結構調整)。
??c) 跨進程回調/協調?
* 一些復雜的跨應用協作可以通過ContentProvider的`query()`、`insert()`等操作來觸發。例如,應用A通過調用應用B的ContentProvider插入一條特定數據,可以作為一種信號通知應用B執行某項操作(不過Intent廣播和Service是更直接的IPC方式)。
??d) 應用初始化 - Auto-initialization Providers?
* **這是非常關鍵且容易被問到的點!** 你在開發中可能會遇到一些第三方庫(或Android Jetpack庫),需要在應用啟動(`Application.onCreate()`之前)就立即執行初始化。**ContentProvider 是實現這種“自動初始化”的核心機制。**
* **原理:** 系統會在應用進程啟動時,**早于**創建 `Application` 實例并調用其 `onCreate()` 方法之前,先**實例化**并**初始化**(調用 `onCreate()` 方法)清單文件中所有在`<application>`標簽里聲明的ContentProvider。
* **庫的做法:** 一個需要預初始化的庫可以在其 SDK 中包含一個**不暴露任何實際數據 URI 的“空” ContentProvider**。1. 這個 Provider 在清單文件中聲明。2. 它的唯一目的是在其 `onCreate()` 方法中執行庫所需的初始化代碼(如初始化單例、設置全局配置、數據庫設置等)。3. 由于其 `onCreate()` 調用時機非常早(在 `Application.onCreate()` 之前),庫就能確保在應用的任何代碼運行前完成初始化。
* **常見例子:*** **WorkManager (>= v2.1):** 使用 `WorkManagerInitializer` Provider 來自動初始化。* **Firebase:** `FirebaseApp`的初始化通常通過Provider(如`FirebaseInitProvider`)完成。* **Google Play Services SDKs:** 某些服務可能使用此機制。* **Mockito / Robolectric 等測試框架:** 有時用于注入測試依賴。
* **優勢:** 開發者只需引入庫依賴并配置清單文件(通常是自動合并的),無需在 `Application.onCreate()` 中顯式調用 `initialize()` 方法,減少了初始化配置遺漏的可能性。
* **注意:** 過度使用會略微增加啟動時間,因為系統需要加載和初始化這些 Provider。應僅用于必要的庫初始化。
代碼示例:?
利用 ContentProvider 實現應用初始化
?自定義初始化 Provider 實現 (空內容提供者)
public class AppInitProvider extends ContentProvider {@Overridepublic boolean onCreate() {// 系統在 Application.onCreate() 前調用initializeApp();return true; // 初始化成功}private void initializeApp() {// 1. 初始化單例配置AppConfig.initialize(getContext());// 2. 設置全局異常處理Thread.setDefaultUncaughtExceptionHandler(new AppCrashHandler());// 3. 初始化網絡請求庫OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).build();RetrofitManager.init(client);// 4. 初始化數據庫框架Room.databaseBuilder(getContext(), AppDatabase.class, "app-db").fallbackToDestructiveMigration().build();// 5. 三方庫初始化 (示例)FirebaseApp.initializeApp(getContext());}// 空方法 - 此 Provider 不提供實際數據@Nullable @Override public Cursor query(...) { return null; }@Nullable @Override public Uri insert(...) { return null; }@Override public int delete(...) { return 0; }@Override public int update(...) { return 0; }@Nullable @Override public String getType(Uri uri) { return null; }
}
?AndroidManifest.xml 配置
<application><!-- 系統將優先初始化此 Provider --><providerandroid:name=".init.AppInitProvider"android:authorities="${applicationId}.appinit" <!-- 唯一標識符 -->android:exported="false" <!-- 不對外暴露 -->android:initOrder="100" <!-- 多個 Provider 時指定初始化順序 -->tools:ignore="ExportedContentProvider"/> <!-- 常規 Application 類 --><activityandroid:name=".MainActivity".../>
</application>
?Application 類中的后續初始化
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 此時 AppInitProvider 已完成初始化// 1. 初始化必須依賴前序操作的組件initializePushNotifications();// 2. 驗證初始化狀態if(!AppConfig.isInitialized()) {throw new RuntimeException("初始化失敗! 請檢查AppInitProvider");}}private void initializePushNotifications() {// 依賴 AppInitProvider 中初始化的網絡庫PushService.setHttpClient(RetrofitManager.getClient());}
}
使用此模式后,開發者只需添加依賴庫,?零代碼初始化三方庫? - 特別適合SDK開發者。如 Firebase 通過此機制實現完全自動初始化,無需在 Application 中調用?FirebaseApp.initializeApp()
。?
Service 啟動方式與實戰場景詳解
????????Service是Android執行后臺長時操作(如音樂播放、網絡下載)或跨進程通信(通過AIDL)的核心組件,其生命周期由系統管理,分為啟動狀態(startService()
)和綁定狀態(bindService()
),8.0+系統需結合前臺服務或JobScheduler規避后臺限制。?
一、兩種核心啟動方式
1. startService() 啟動方式
?本質特點?:
- 啟動后服務與組件完全解耦?
- 生命周期獨立運行
- ?必須顯式停止?(調用 stopSelf() 或 stopService())
?完整生命周期流程?:
- 首次啟動:
onCreate()
?→?onStartCommand()
- 后續啟動:直接觸發?
onStartCommand()
- 停止服務:
onDestroy()
?場景適用?:
- ?后臺長期任務?:如音樂播放、定位追蹤
- ?無交互任務?:如日志上傳、數據同步
- ?跨應用操作?:如推送消息處理
?實戰代碼?:
// 啟動下載服務
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.putExtra("file_url", "https://example.com/file.zip");
startService(downloadIntent);// 服務內部停止
public class DownloadService extends Service {@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {new Thread(() -> {downloadFile(intent.getStringExtra("file_url"));stopSelf(); // 下載完成后自動停止}).start();return START_NOT_STICKY;}
}
2. bindService() 啟動方式
?本質特點?:
- 建立組件與服務的綁定關系?
- 通過 IBinder 接口實現雙向通信?
- 綁定解綁自動管理生命周期
?完整生命周期流程?:
- 首次綁定:
onCreate()
?→?onBind()
- 通信期間:通過 Binder 接口交互
- 所有綁定解除:
onUnbind()
?→?onDestroy()
?場景適用?:
- ?實時交互場景?:如音樂控制(播放/暫停/進度)
- ?功能模塊解耦?:如支付模塊服務
- ?跨進程通信(IPC)??:不同應用間的數據交換
?實戰代碼?:
// 綁定音樂服務
ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {MusicService.MusicBinder musicBinder = (MusicService.MusicBinder) binder;musicBinder.play(); // 直接調用服務方法}
};
bindService(new Intent(this, MusicService.class), conn, BIND_AUTO_CREATE);// 解綁服務
unbindService(conn);
Service使用場景
音樂/視頻播放服務
?核心實現方案?:
public class MediaPlaybackService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {private MediaPlayer mediaPlayer;private MediaSession mediaSession;private static final int NOTIFICATION_ID = 101;@Overridepublic void onCreate() {// 初始化媒體播放器mediaPlayer = new MediaPlayer();mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());// 創建媒體會話(支持鎖屏控制)mediaSession = new MediaSession(this, "MediaPlaybackService");mediaSession.setCallback(new MediaSessionCallback());mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);// 設置通知通道(Android 8.0+必須)createNotificationChannel();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {String action = intent.getStringExtra("action");if("PLAY".equals(action)) {String mediaUrl = intent.getStringExtra("media_url");playMedia(mediaUrl);} else if("PAUSE".equals(action)) {pausePlayback();}return START_NOT_STICKY;}private void playMedia(String mediaUrl) {try {// 停止當前播放if (mediaPlayer.isPlaying()) {mediaPlayer.stop();}mediaPlayer.reset();mediaPlayer.setDataSource(mediaUrl);mediaPlayer.prepareAsync(); // 異步準備播放mediaPlayer.setOnPreparedListener(this);// 轉換為前臺服務Notification notification = buildMediaNotification("正在播放");startForeground(NOTIFICATION_ID, notification);} catch (IOException e) {Log.e("MediaService", "播放初始化失敗", e);}}@Overridepublic void onPrepared(MediaPlayer mp) {mediaPlayer.start();mediaSession.setActive(true);// 更新通知顯示播放狀態Notification notification = buildMediaNotification("正在播放");NotificationManager nm = getSystemService(NotificationManager.class);nm.notify(NOTIFICATION_ID, notification);}// 構建媒體通知(含播放控制按鈕)private Notification buildMediaNotification(String status) {// 構建通知內容(包含播放控制按鈕)// ...}
}
關鍵技術與注意事項?:
- ?音頻焦點管理?:
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(focusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- ?耳機事件處理?:
// 注冊耳機拔出廣播
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(noisyReceiver, intentFilter);BroadcastReceiver noisyReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if(AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {pausePlayback(); // 耳機拔出時暫停播放}}
};
- ?通知欄控制?:
- 通過 MediaStyle 通知樣式添加播放控制按鈕
- 更新播放進度(Android 12+支持直接顯示播放進度條)
- ?播放狀態持久化?:
- 在 onDestroy() 中保存當前播放位置
- 在 onCreate() 中恢復最后播放位置