Android第十三次面試總結(四大 組件基礎)

Activity生命周期和四大啟動模式詳解

一、Activity 生命周期

Activity 的生命周期由一系列回調方法組成,用于管理其創建、可見性、焦點和銷毀過程。以下是核心方法及其調用時機:

  1. ?onCreate()??

    • ?調用時機?:Activity 首次創建時調用。
    • ?作用?:初始化布局(setContentView)、綁定數據、創建后臺線程等。
    • ?注意?:在此方法中應避免耗時操作。
  2. ?onStart()??

    • ?調用時機?:Activity 可見但未獲得焦點(例如被對話框覆蓋)。
    • ?作用?:恢復UI更新或資源加載。
  3. ?onResume()??

    • ?調用時機?:Activity 進入前臺并可與用戶交互。
    • ?作用?:啟動動畫、傳感器監聽、高頻率更新UI等。
    • ?關鍵點?:此時 Activity 位于棧頂。
  4. ?onPause()??

    • ?調用時機?:Activity 失去焦點(如彈出對話框或跳轉到其他 Activity)。
    • ?作用?:保存臨時數據、釋放資源(如攝像頭)。
    • ?注意?:需快速執行,否則會影響新 Activity 的啟動。
  5. ?onStop()??

    • ?調用時機?:Activity 完全不可見(被其他 Activity 覆蓋或退出)。
    • ?作用?:停止動畫、釋放非必要資源。
  6. ?onDestroy()??

    • ?調用時機?:Activity 被銷毀(用戶主動退出或系統回收資源)。
    • ?作用?:清理內存、注銷廣播等。
  7. ?onRestart()??

    • ?調用時機?:Activity 從停止狀態重新回到前臺(如按返回鍵返回)。
    • ?流程?:onRestart()onStart()onResume()

場景應用

場景 1:打開新頁面?

?流程?:

  1. 原 Activity 執行?onPause()(失去焦點)
  2. 新 Activity 依次執行:
    • onCreate()(初始化)
    • onStart()(可見)
    • onResume()(可交互)
  3. 原 Activity 執行?onStop()(完全不可見)

?典型場景?:

  • 從主頁跳轉到詳情頁
  • 列表頁打開新的商品頁

?場景 2:返回上一個頁面?

?流程?:

  1. 當前 Activity 執行?onPause()
  2. 上一個 Activity 依次執行:
    • onRestart()(重新激活)
    • onStart()(可見)
    • onResume()(可交互)
  3. 當前 Activity 執行:
    • onStop()
    • onDestroy()(被銷毀)

?典型場景?:

  • 提交訂單后返回購物車
  • 查看圖片詳情后返回相冊

?場景 3:屏幕旋轉?

?流程?:

  1. onPause()?→?onSaveInstanceState()(保存數據)
  2. onStop()?→?onDestroy()(銷毀實例)
  3. 重新創建:
    • 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個關鍵點):

  1. ?復用已有頁面?

    • 避免重復創建相同 Activity(節省內存)
    • 保持當前頁面狀態(如滾動位置、輸入內容)
  2. ?更新數據?

    protected void onNewIntent(Intent intent) {setIntent(intent); // 必須更新!否則getIntent()拿舊數據refreshUI(intent); // 根據新數據刷新界面
    }
  3. ?特定場景觸發?

    • ?singleTop?:當 Activity 位于棧頂時
    • ?singleTask/singleInstance?:當 Activity 已存在于棧中時

?典型場景?:

  • 點擊通知欄多次打開同一聊天頁(直接刷新消息)
  • 從支付結果頁返回后再次支付(復用頁面更新訂單)
  • 全局搜索框重復搜索(保留搜索歷史記錄)

?記住一個原則?:
只要看到?singleTop/singleTask,就要考慮是否需要重寫?onNewIntent()?處理數據刷新!


啟動模式在后臺運行時的關鍵行為解析

一、后臺啟動 Activity 的通用規則
  1. ?基本行為?:

    • 當應用在后臺時啟動新 Activity,系統會先將應用帶回前臺
    • 新 Activity 會被添加到當前任務棧中
    • 用戶按返回鍵時會回到上一個 Activity
  2. ?核心影響?:

    • 應用進程優先級提升到前臺進程
    • 可能觸發系統回收機制(低內存時后臺進程優先被殺)

二、后臺啟動的注意事項

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. 最佳實踐
  1. ?后臺啟動 singleTask 主頁?:

    // 清理所有歷史棧
    Intent intent = new Intent(context, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
  2. ?避免后臺 standard 啟動?:

    • 易導致 OOM(內存溢出)
    • 返回棧混亂
  3. ?跨進程通信?:

    // 啟動獨立進程的Activity
    <activity android:process=":camera_process"/>

三、各模式后臺表現對比表

啟動模式后臺啟動特點內存效率適用場景
standard持續堆疊新實例需多實例的普通頁面
singleTop棧頂復用省資源通知/消息更新
singleTask清理棧內多余頁面應用主頁/核心入口
singleInstance獨立進程不受主應用影響最高相機/電話等系統級功能
模式實例數量棧位置典型場景
standard多個當前棧普通頁面
singleTop棧頂唯一當前棧防重復啟動(通知欄)
singleTask棧內唯一可指定新棧應用主界面
singleInstance全局唯一獨占新棧獨立功能(如相機)

微信小游戲雙圖標背后的啟動模式解析

場景重現:

  1. 點擊微信圖標啟動微信主界面

  2. 在微信內點擊進入小游戲

  3. ?后臺出現兩個獨立圖標?:

    • 微信主應用圖標

    • 小游戲獨立圖標

核心啟動模式:singleInstance

?實現原理?:

<!-- 小游戲Activity聲明示例 -->
<activityandroid:name=".GameActivity"android:launchMode="singleInstance"android:taskAffinity="com.tencent.game"android:process=":game_process" />

?三大關鍵機制?:

  1. ?獨立進程?

    android:process=":game_process"
    • 小游戲運行在獨立的 com.tencent.mm:game_process 進程

    • 與微信主進程 com.tencent.mm 完全隔離

    • ?效果?:系統顯示兩個獨立進程圖標

  2. ?獨立任務棧?

    android:taskAffinity="com.tencent.game"
    android:launchMode="singleInstance"
    • 創建專屬任務棧(與微信主棧隔離)

    • ?效果?:

      • 最近任務顯示兩個獨立任務項

      • 游戲退出時直接回到手機桌面,不經過微信

  3. ?跨進程通信?

    // 微信啟動游戲的關鍵代碼
    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 允許多實例

完整啟動流程:

  1. ?用戶點擊小游戲入口?

  2. ?微信創建子進程?:

    • 通過 startActivity() 跨進程啟動

    • 指定 singleInstance + NEW_TASK 標志

  3. ?系統創建資源隔離區?:

    • 獨立內存空間(分配專屬內存)

    • 獨立渲染線程(避免微信主線程卡頓)

    • 獨立任務棧(系統記錄為獨立應用)

  4. ?游戲結束后?:

    • 游戲進程銷毀

    • 微信主進程不受影響

    • ?返回路徑?:游戲 → 桌面 (不返回微信)

技術優勢:

  1. ?性能隔離?:

    • 游戲占500MB內存不影響微信聊天

    • 游戲崩潰不會導致微信閃退

    # 進程內存占用示例
    com.tencent.mm: 300MB   # 微信主進程
    com.tencent.mm:game: 500MB # 游戲進程
  2. ?獨立生命周期?:

    操作

    微信主進程

    游戲進程

    進入游戲

    onPause()

    onCreate()

    游戲切后臺

    -

    onPause->onStop

    關閉游戲

    onResume()

    onDestroy()

    游戲崩潰

    無影響

    自動重啟

  3. ?用戶體驗優化?:

    • 游戲可獨立操作(微信后臺保持運行)

    • 小窗口模式雙向互動(微信浮窗+游戲)

對比其他啟動模式:

啟動模式

是否分進程

是否獨立圖標

適用場景

standard

?

?

普通頁面跳轉

singleTask

?

?

微信錢包

singleTop

?

?

公眾號文章

?singleInstance?

?

?**?**?

小游戲/視頻通話

典型應用場景:

  1. 微信/QQ內置小游戲

  2. 直播平臺連麥功能

  3. 銀行App的安全鍵盤

  4. 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())

?完整生命周期流程?:

  1. 首次啟動:onCreate()?→?onStartCommand()
  2. 后續啟動:直接觸發?onStartCommand()
  3. 停止服務: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 接口實現雙向通信?
  • 綁定解綁自動管理生命周期

?完整生命周期流程?:

  1. 首次綁定:onCreate()?→?onBind()
  2. 通信期間:通過 Binder 接口交互
  3. 所有綁定解除: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) {// 構建通知內容(包含播放控制按鈕)// ...}
}

關鍵技術與注意事項?:

  1. ?音頻焦點管理?:
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(focusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
  1. ?耳機事件處理?:
// 注冊耳機拔出廣播
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(); // 耳機拔出時暫停播放}}
};
  1. ?通知欄控制?:
  • 通過 MediaStyle 通知樣式添加播放控制按鈕
  • 更新播放進度(Android 12+支持直接顯示播放進度條)
  1. ?播放狀態持久化?:
  • 在 onDestroy() 中保存當前播放位置
  • 在 onCreate() 中恢復最后播放位置

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/84483.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/84483.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/84483.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

講講JVM的垃圾回收機制

垃圾回收就是對內存堆中已經死亡或者長時間沒有使用的對象進行清楚或回收。 JVM 在做 GC 之前&#xff0c;會先搞清楚什么是垃圾&#xff0c;什么不是垃圾&#xff0c;通常會通過可達性分析算法來判斷對象是否存活。 在確定了那些垃圾可以被回收后&#xff0c;垃圾回收器&…

QT軟件外包開發費用

國內QT軟件外包開發費用是一個非常復雜的問題&#xff0c;沒有一個固定的價格&#xff0c;它受到多種因素的影響。以下將詳細闡述影響QT軟件外包開發費用的主要因素&#xff0c;并提供大致的價格區間供參考&#xff08;請注意&#xff0c;這些價格僅為估算&#xff0c;實際報價…

iOS 16 SwiftUI 優雅跳轉實踐:用枚舉路由和 NavigationStack 實現多頁面導航

引言&#xff1a;跳轉的混亂與優雅的必要性 SwiftUI 給我們帶來了聲明式界面的全新開發體驗&#xff0c;但當涉及到頁面跳轉時&#xff0c;許多開發者仍然面臨一些“舊痛”。最初的 NavigationLink(destination:isActive:) 或 sheet(isPresented:) 等方式雖然能用&#xff0c;…

TikTok矩陣養號實戰:住宅IP純凈度與設備指紋聯動方案

在TikTok矩陣運營中&#xff0c;住宅IP純凈度和設備指紋管理是規避風控的核心。以下方案整合多平臺風控邏輯與實戰數據&#xff0c;覆蓋環境隔離、行為模擬到風險防控全流程。 &#x1f527; 一、住宅IP純凈度維持策略 IP篩選與驗證 靜態住宅IP優選&#xff1a;核心賬號綁定目標…

Elasticsearch增刪改查語句

創建索引庫&#xff1a;不帶映射的 PUT /索引名稱 {"settings": {"number_of_shards": 3, // 主分片數"number_of_replicas": 1 // 每個主分片的副本數} } 創建帶映射的索引庫&#xff1a; PUT /products {"settings": {"…

樹莓派4B, ubuntu20.04, 安裝Ros Noetic[踩坑記錄]

一、安裝過程 1. 硬件要求 樹莓派4B (建議4GB或8GB內存版本) 至少16GB的microSD卡 2. 下載并安裝Ubuntu 20.04 Ubuntu 20.04 LTS (Focal Fossa) for Raspberry Pi 使用Raspberry Pi Imager或BalenaEtcher將鏡像寫入microSD卡 3. 安裝ROS Noetic ?# 設置sources.list s…

視覺slam--框架

視覺里程計的框架 傳感器 VO--front end VO的缺點 后端--back end 后端對什么數據進行優化 利用什么數據進行優化的 后端是怎么進行優化的 回環檢測 建圖 建圖是指構建地圖的過程。 構建的地圖是點云地圖還是什么信息的地圖&#xff1f; 建圖并沒有一個固定的形式和算法…

每日算法 -【Swift 算法】刪除鏈表的倒數第 N 個結點

?? Swift | 刪除鏈表的倒數第 N 個結點(含詳細注釋) 在刷算法題時,我們經常會遇到關于鏈表的題目,而「刪除鏈表的倒數第 N 個節點」是其中一個非常經典的題。今天我們就用 Swift 來實現它,并梳理清楚整個思路。 ?? 一、題目描述 給你一個鏈表,刪除鏈表的倒數第 n 個…

Truffle 和 Ganache 使用指南

Truffle 和 Ganache 使用指南 Truffle 命令詳解 Truffle 是一個流行的以太坊開發框架,提供了許多有用的命令來簡化智能合約的開發、測試和部署。 常用 Truffle 命令 初始化項目 truffle init 創建一個新的 Truffle 項目結構。 編譯合約 truffle compile 編譯項目中的 Solid…

docker進階之架構

一、OCI 名為OCI&#xff0c;全稱 Open Container Initiative/開放容器倡議,其目的主要是為了制定容器技術的通用技術標準。目前主要有兩種標準&#xff1a; 1、容器運行時標準 &#xff08;runtime spec&#xff09; 2、容器鏡像標準&#xff08;image spec&#xff09; …

企業產品網絡安全日志6月10日-WAF資費消耗排查

發生了什么事&#xff1f; 上個的費用賬單出來了&#xff0c;WAF費用有點飆升。比平時多了50%到100%。 周五的時候就已經知道這個事情了&#xff0c;但當時考慮肯定是攔截了一些惡意請求&#xff0c;所以。 反正也是上個月的事情了&#xff0c;所以周一過來復盤一下 數了下&a…

vue3+el-table 利用插槽自定義數據樣式

<el-table-column label"匹配度" prop"baseMatchingLevel"><template #default"scope"><div :style"{ color: scope.row.baseMatchingLevel > 0.8 ? #00B578 : #FA5151 }">{{ scope.row.baseMatchingLevel }}&l…

[密碼學實戰]C語言使用SDF庫構建國密算法RESTful服務(五)

[密碼學實戰]C語言使用SDF庫構建國密算法RESTful服務(五) 引言 在現代信息安全領域,國密算法(SM系列算法)作為中國自主研發的密碼算法標準,在金融、政務等領域得到廣泛應用。本文將詳細介紹如何使用C語言結合SDF(Security Device Function)庫,構建一個提供國密算法服…

ubuntu 22.04搭建SOC開發環境

目錄 AArch64位編譯器命名規則 安裝交叉工具鏈編譯 安裝aarch64-none-elf工具鏈 安裝aarch64-none-linux-gnu工具鏈 啟動板載系統 板卡啟動方式 硬件連接 準備階段 硬件連接 udev規則配置 啟動流程 開發板外觀圖 硬件準備清單 硬件連接 SSH登錄系統 設置Windows為…

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特點代碼演示 push和pop特點代碼演示 前言 在 iOS 開發中&#xff0c;push 和 present 是兩種不同的視圖控制器切換方式&#xff0c;它們有著顯著的區別。 present和dismiss 特點 在當前控制器上方新建視圖層級需要手動調用…

Java項目中常用的中間件及其高頻問題避坑

Java項目中常用的中間件及其高頻問題避坑如下: 一、常用中間件分類及作用 1. ??消息隊列中間件?? ??作用??:解耦系統、異步通信、削峰填谷。??代表產品??: ??Kafka??:高吞吐量流處理,適合日志收集、實時分析。??RocketMQ??:金融級可靠性,支持事務消…

發布一個angular的npm包(包含多個模塊)

為什么要發布npm包 根本原因時為了能夠在更廣泛的區域復用代碼&#xff0c;比如公司不支持一般的外部網絡&#xff0c;但是支持npm包的下載&#xff0c;那么就可以發布npm包&#xff0c;然后在公司內使用。 angular的npm不同嗎 angular library angular 目前已經到angular20…

Web后端基礎:Maven基礎

課程內容&#xff1a; 初始MavenMaven概述 Maven模型Maven倉庫介紹Maven安裝與配置 IDEA集成Maven依賴管理單元測試 1.初始Maven 1.1介紹 Maven 是一款用于管理和構建Java項目的工具&#xff0c;是Apache旗下的一個開源項目 。 Apache 軟件基金會&#xff0c;成立于1999年7月…

http協議同時傳輸文本和數據的新理解

首先&#xff0c;承認本人對于http協議認知確實不夠&#xff0c;從來沒有仔細研究這一塊。 其次&#xff0c;這回確實要把自己十幾年的理解更新一下了&#xff0c;主要還是自己過去沒有認真研究過http協議。 這一次是這么回事&#xff0c;碰到一個情況&#xff0c;要在一次消…

《安富萊嵌入式周報》第354期: 開源36通道16bit同步數據采集卡,開源PoE以太網GPIB,分體式鍵盤DIY,微軟WSL開源,USB轉車載以太網

周報匯總地址&#xff1a;嵌入式周報 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬漢嵌入式論壇 - Powered by Discuz! 視頻版&#xff1a; https://www.bilibili.com/video/BV1kJThzxETY/ 《安富萊嵌入式周報》第354期: 開源36通道16bit同…