Android 底層實現基礎

Activity 生命周期

在這里插入圖片描述

應用內 Activity 跳轉流程(A → B)

從 Activity A 打開新的 Activity B(如點擊按鈕跳轉詳情頁)

  1. A.onCreate()A.onStart()A.onResume() (A 已在前臺)
  2. 點擊跳轉按鈕 → A.onPause() (A 暫停但仍可見)
  3. B.onCreate()B.onStart()B.onResume() (B 進入前臺)
  4. A.onStop() (A 完全不可見,但未被銷毀)

在這里插入圖片描述


返回鍵關閉當前 Activity(B → A)

在 Activity B 中按返回鍵,回到 Activity A

  1. 按返回鍵 → B.onPause()
  2. A.onRestart()A.onStart()A.onResume() (A 重新可見)
  3. B.onStop()B.onDestroy() (B 被銷毀)

在這里插入圖片描述


Home 鍵切到后臺(應用存活)

在 Activity A 運行時按 Home 鍵回到桌面

  1. 按 Home 鍵 → A.onPause()A.onStop()
    注意:此時 A 未被銷毀,進程存活)

切換到其他應用(如從微信跳轉到支付寶)

從當前應用 Activity A 打開另一個應用(如點擊鏈接跳轉支付寶)

  1. 點擊跳轉 → A.onPause()
  2. 支付寶冷/溫啟動 → 支付寶頁面顯示
  3. A.onStop() (A 完全不可見,但進程存活)

后臺被系統回收后恢復(溫啟動場景)

應用在后臺時,因內存不足被系統回收 Activity(非殺進程),用戶再次點擊圖標進入

  1. 系統回收 Activity → 調用 A.onSaveInstanceState() 保存數據
  2. 用戶點擊圖標 → 重建 Activity A
    A.onCreate(savedInstanceState)A.onStart()A.onResume()

返回鍵退出應用(銷毀所有 Activity)

在首頁 Activity A 按返回鍵退出應用

  1. 按返回鍵 → A.onPause()A.onStop()A.onDestroy()
  2. 進程仍存活(系統緩存),但任務棧清空

任務(Task)和返回棧(Back Stack)

一、核心概念

  1. 任務(Task)

    • 本質:用戶為完成特定目標(如“寫郵件”、“購物”)而交互的 Activity 集合
    • 表現形式:一個按打開順序排列的 Activity 棧(即返回棧)
    • 系統級標識:每個任務有獨立 任務 ID,系統通過它管理任務切換。
    • 用戶視角:在“最近任務列表”(Recents Screen)中顯示為獨立卡片。
  2. 返回棧(Back Stack)

    • 本質:屬于同一任務的 Activity 實例的有序棧(后進先出)。
    • 關鍵規則:用戶按返回鍵時,棧頂 Activity 出棧并銷毀,前一個 Activity 恢復顯示。
    • 跨進程支持:棧內 Activity 可來自不同應用(如從瀏覽器打開地圖應用)。

二、底層工作原理

1. Activity 啟動與入棧
  • 默認行為:新啟動的 Activity 被壓入當前任務的棧頂(standard 啟動模式)。
  • 任務親和性(Task Affinity)
    • 每個 Activity 通過 android:taskAffinity 屬性聲明“歸屬偏好”。
    • 默認親和性 = 應用包名(同一應用 Activity 通常屬于同一任務)。
  • Intent Flags 控制棧行為(代碼動態控制):
    • FLAG_ACTIVITY_NEW_TASK:在新任務中啟動 Activity(若任務不存在則創建)。
    • FLAG_ACTIVITY_CLEAR_TOP:若目標 Activity 已在棧中,則清除其上的所有 Activity。
    • FLAG_ACTIVITY_SINGLE_TOP:若目標 Activity 已在棧頂,則復用實例(觸發 onNewIntent())。
2. 啟動模式(Launch Modes)
模式行為描述測試關注點
standard (默認)每次啟動創建新實例,壓入當前棧。多實例場景下的狀態一致性(如填寫表單)。
singleTop若目標 Activity 在棧頂,則復用實例(觸發 onNewIntent());否則創建新實例。通知欄點擊打開已存在的頁面時是否刷新數據。
singleTask系統創建新任務或將 Activity 移至現有任務根部。同一任務只存在一個實例。多任務邊界、深度鏈接跳轉后的返回路徑是否異常。
singleInstance獨占整個任務,該任務僅容納此一個 Activity。與其他應用的交互(如相機調用),返回棧隔離性。
3. 任務管理機制
  • 最近任務列表(Recents)
    • 系統維護任務快照(縮略圖 + 描述)。
    • 移除任務卡片會清除整個返回棧(所有 Activity 銷毀)。
  • 任務重用(Re-parenting)
    • 當從應用 A 啟動應用 B 的 Activity 時:
      • 若 B 已有任務在后臺,該 Activity 會移入 B 的任務棧
      • 返回鍵會先回退到 B 的前一個 Activity,而非回到 A。
  • 后臺任務回收
    • 系統內存不足時,按 LRU 規則銷毀后臺任務棧(保留狀態 Bundle 以便重建)。

進程間通信規則

核心思想: 應用運行在獨立的進程(沙盒)中,無法直接訪問彼此的內存。IPC 提供一種安全的“郵遞”機制,讓應用可以發送請求(消息、數據、方法調用)并接收響應。

底層核心機制:Binder

  1. 建立郵箱(Binder 驅動): 操作系統內核提供了一個中央“郵局”(Binder 驅動)。所有需要通信的應用(進程)都向這個郵局注冊自己的“郵箱地址”(Binder 引用)。
  2. 寫信(序列化): 發送方應用(客戶端)將想要傳遞的數據或方法調用請求(包括方法名、參數)序列化成一個線性格式(通常使用 Parcel)。想象成把信息寫在紙上。
  3. 投遞到郵局(系統調用): 客戶端通過系統調用(ioctl)將打包好的 Parcel 發送給 Binder 驅動。這個調用會指定目標“郵箱地址”(目標服務的 Binder 引用)。
  4. 郵局分揀(內核處理): Binder 驅動在內核空間接收到數據包。它根據目標引用找到接收方應用(服務端)對應的進程和線程信息。
  5. 派送信件(喚醒目標線程): Binder 驅動將數據包放入接收方進程的一個專屬接收隊列中,并喚醒服務端進程中負責處理 IPC 的線程(通常是主線程或 Binder 線程池中的一個線程)。
  6. 拆信(反序列化): 服務端線程被喚醒,從隊列中取出 Parcel,將數據反序列化回原始格式(方法名、參數)。
  7. 處理請求(執行方法): 服務端根據方法名找到對應的實現代碼,使用反序列化得到的參數執行該方法。
  8. 寫回信(序列化結果): 服務端將方法執行的結果(或異常)再次序列化Parcel
  9. 回信投遞(系統調用): 服務端通過另一個系統調用將結果 Parcel 發送回 Binder 驅動
  10. 郵局送回(內核處理): Binder 驅動將結果包放入客戶端進程的接收隊列,并喚醒等待結果的客戶端線程。
  11. 客戶端收信(反序列化結果): 客戶端線程被喚醒,取出結果 Parcel反序列化得到最終結果或異常。
  12. 客戶端處理結果: 客戶端繼續執行,使用收到的結果。

隱式/顯式 Intent

  1. 顯式 Intent (點名道姓):

    • 明確知道要啟動哪個“人”(組件)干活。
    • 直接告訴系統:“啟動 包名 com.example.app 里 類名 com.example.app.MyActivity 這個 Activity!”
    • 用在: 啟動自己 App 內部的界面 (Activity)、服務 (Service) 等,或者明確知道另一個 App 里具體哪個組件(需要知道包名和類名)。
    • 優點: 精準、高效。
    • 缺點: 必須知道具體目標,跨 App 啟動需要對方暴露組件信息(有時不推薦)。
  2. 隱式 Intent (發廣播招人):

    • 只知道要干什么“活”(操作),但不知道誰干。
    • 告訴系統:“我要 查看一張圖片 (Action=VIEW, Data=圖片URI, Type=image/*)!” 或者 “我要 發送一封郵件 (Action=SEND, Type=text/plain)!”
    • 系統怎么做: 系統拿著你的“招聘要求”(Action, Data, Type, Category等),去查所有 App 的“簡歷”(在 AndroidManifest.xml 中聲明的 <intent-filter>)。找到所有符合條件的組件。
    • 結果:
      • 如果只有一個組件符合:直接啟動它。
      • 如果有多個符合:彈出選擇器 (Chooser) 讓用戶選一個。
      • 如果沒找到:啟動失敗。
    • 用在: 啟動系統功能(拍照、打電話、選擇聯系人)、分享內容、打開特定類型文件、讓其他 App 提供特定服務等。跨 App 協作的主要方式。
    • 優點: 靈活、解耦。你的 App 不需要知道具體誰來處理。
    • 缺點: 控制權較低(用戶可能選錯 App),性能略低(需要系統匹配)。
特征顯式 Intent (Explicit Intent)隱式 Intent (Implicit Intent)
目標指定點名道姓! setComponent(), setClass()new Intent(Context, Class) 明確指定要啟動哪個 App 的哪個 Activity/Service 等。只提要求! 通過 action (動作,如打電話、發郵件、查看)、data (數據,如網址、電話號碼) 和 category (類別) 描述你想做什么
定位方式精準定位。 就像你知道朋友的具體門牌號去找他。廣播找人。 就像你在廣場喊“誰會修電腦?”,會修的人(組件)自己響應。
作用范圍通常用于啟動自己 App 內部的組件。 因為你知道組件的具體名字。用于啟動自己 App 內部或其他 App 的組件。 是實現不同 App 之間協作的關鍵。
系統處理系統直接啟動你指定的那個組件。系統查找所有聲明了能處理該 Intent 要求的 (action + data + category) 的組件,如果有多個,會讓用戶選擇(選擇器)。
典型用途App 內部頁面跳轉、啟動自己 App 的后臺 Service。打開網頁、打電話、發郵件、分享內容、選擇圖片、使用地圖等跨 App 或系統級功能
關鍵優勢精準、高效、安全(不易被劫持)。靈活、解耦、支持跨應用。
關鍵風險只能啟動已知組件,靈活性差。可能找不到匹配組件導致崩潰(需用 resolveActivity() 檢查),或有多個匹配時用戶需要選擇

一句話總結:

  • 顯式 Intent:張三,你去把這事辦了!” (指定具體組件)
  • 隱式 Intent:誰能辦這事? 來個人把它辦了!” (聲明需求,系統找匹配者)

關鍵底層點簡化:

  • 顯式 Intent 直接調用目標組件,不經過系統匹配。
  • 隱式 Intent 依賴系統在安裝時收集所有 App 的 <intent-filter> 信息(存儲在 PackageManager 數據庫里)。啟動時,系統根據 Intent 里的信息(主要是 Action + Data/Type)去數據庫里快速查找匹配的組件。

View系統與事件分發機制

一、 View 系統:UI 的構建基石

  1. 樹形結構:

    • 所有 UI 元素 (Button, TextView, ImageView, 甚至 LinearLayout, RelativeLayout) 都是 View 或其子類 (ViewGroup)。
    • ViewGroup 是特殊的 View,可以包含其他 View (子 View) 或 ViewGroup (子 ViewGroup)。
    • 整個界面是一棵由 ViewViewGroup 組成的樹狀結構,最頂層通常是 DecorView (包含狀態欄、標題欄、內容區域),根部是 ActivityWindow
  2. 核心流程:

    • 測量 (Measure): 父 View (ViewGroup) 詢問每個子 View:“你需要多大空間?” (考慮自身尺寸要求 wrap_content/match_parent/固定值 和父 View 的約束)。這是一個遞歸過程,從根 View 開始向下遍歷整棵樹。
    • 布局 (Layout): 父 View (ViewGroup) 根據測量結果,告訴每個子 View:“你被放在哪里 (左上右下坐標)”。這也是遞歸過程。
    • 繪制 (Draw): 每個 View 負責繪制自己到屏幕上指定的矩形區域。流程是從根 View 開始,先繪制背景,再繪制自己內容 (onDraw),然后遞歸繪制它的所有子 View。遵循順序:父 View 在底層 -> 子 View 在上層
  3. 關鍵角色:

    • View UI 基本單元,負責自身繪制和響應觸摸事件
    • ViewGroup 特殊的 View,核心職責是容納和管理子 View
      • 測量子 View (詢問大小)。
      • 擺放子 View (決定位置)。
      • 管理事件分發 (決定哪個子 View 能處理觸摸事件)。

二、 事件分發機制:觸摸事件的旅程

  1. 事件源頭: 用戶觸摸屏幕產生一個 MotionEvent 對象 (包含觸摸坐標、動作類型如 ACTION_DOWN/MOVE/UP 等)。

  2. 分發目標: 事件需要找到能“消費” (處理) 它的 View

  3. 傳遞路徑: 事件從根 View (通常是 DecorView) 開始,沿著 View 樹自上而下傳遞。

    • 事件首先到達最頂層的 ViewGroup (Activity 的根布局)。
    • 然后層層向下傳遞到可能的子 ViewGroup 或最終的子 View
  4. 核心方法 (決策點): 事件在 ViewViewGroup 之間傳遞時,關鍵由三個方法決定去向:

    • dispatchTouchEvent(MotionEvent event) 事件分發入口View/ViewGroup 收到事件后首先調用此方法。

      • View: 檢查自身是否可點擊/可處理事件,是則嘗試 onTouchEvent
      • ViewGroup: 核心邏輯所在地! 它決定:
        • 是否攔截 (onInterceptTouchEvent) 事件,不讓子 View 處理。
        • 如果不攔截,則遍歷子 View (通常按 Z 序或添加順序反向遍歷,后添加/上層 View 優先),詢問子 View 是否愿意處理 (dispatchTouchEvent)。
    • onInterceptTouchEvent(MotionEvent event) ViewGroup 獨有!dispatchTouchEvent 內部調用。用于判斷當前 ViewGroup 是否要“截胡” 這個事件序列 (從 DOWNUP/CANCEL)。如果返回 true,后續事件不再分發給子 View,直接交給自身的 onTouchEvent 處理。默認返回 false (不攔截)

    • onTouchEvent(MotionEvent event) 事件處理終點View 或攔截了事件的 ViewGroup 在這里真正嘗試消費 (處理) 事件。如果成功處理 (如點擊了按鈕),返回 true;如果處理不了或不關心,返回 false,事件會向上回溯給父 View 的 onTouchEvent 嘗試處理。

  5. 分發邏輯 (核心流程):

    1. 事件從根 ViewGroupdispatchTouchEvent 開始。
    2. ViewGroup 先調用自己的 onInterceptTouchEvent 看是否攔截。
    3. 如果不攔截
      • 遍歷子 View (通常從最上層的子 View 開始)。
      • 判斷觸摸點是否落在子 View 區域內且子 View 能接收事件。
      • 如果滿足,調用子 View 的 dispatchTouchEvent (遞歸開始)。
    4. 如果攔截所有子 View 都不處理
      • 調用自身的 onTouchEvent 嘗試處理。
    5. 如果自身的 onTouchEvent 也不處理,事件回傳給父 ViewGrouponTouchEvent (向上回溯)。
    6. 如果某個 View 的 onTouchEventACTION_DOWN 時返回 true,表示它消費了這個事件序列,后續的 MOVE/UP 等事件會直接分發給它 (不再詢問 onInterceptTouchEvent,可能跳過中間 ViewGroup 的 dispatch 部分邏輯,但流程更高效),直到序列結束 (UP/CANCEL)。

資源管理與適配機制

核心目標: 讓同一份 App 代碼能優雅地適配不同設備(屏幕尺寸、分辨率、語言、系統版本、橫豎屏、夜間模式等)和用戶配置(字體大小)。

一、 資源管理:組織與訪問

  1. 資源是什么?

    • App 中非代碼的一切:圖片 (drawable)、布局 (layout)、字符串 (string)、顏色 (color)、尺寸 (dimen)、樣式 (style)、菜單 (menu)、動畫 (anim)、原始文件 (raw)、XML 等。
    • 目的: 將 UI 內容、文本、樣式等與 Java/Kotlin 代碼邏輯分離,便于修改、復用和適配。
  2. 資源存放 (res/ 目錄):

    • 按類型分目錄: res/drawable/, res/layout/, res/values/, res/menu/ 等。這是基本組織方式。
    • 關鍵:資源限定符 (Qualifiers): 核心適配機制!
      • 在目錄名后添加后綴來指定資源適用的特定條件
      • 格式: 資源類型-限定符1-限定符2-... (例如:drawable-hdpi, layout-sw600dp-land, values-en-rUS)。
      • 系統自動選擇: 運行時,Android 系統根據設備的當前配置(語言、屏幕尺寸、橫豎屏、夜間模式等),自動選擇最匹配限定符目錄下的資源。如果沒有完全匹配,會尋找最接近的或默認目錄 (drawable/, values/ 等) 的資源。
      • 優先級: 系統按預定義規則評估多個限定符的優先級(如屏幕尺寸優先級高于語言)。
  3. 資源編譯與訪問:

    • 編譯: aapt2 (Android Asset Packaging Tool) 將 res/ 下資源編譯打包進 APK,并生成 R.java (或 R.kt) 文件。
    • 訪問 (代碼中): 通過自動生成的 R 類訪問資源 (如 R.drawable.icon, R.string.app_name, R.layout.activity_main)。
    • 訪問 (XML 中): 使用 @ 符號引用 (如 @drawable/icon, @string/hello, @dimen/padding_medium)。

二、 適配機制:應對多樣性

  1. 屏幕適配:

    • 核心理念:密度無關 (Density-Independent)
      • dp (Density-independent Pixels): 長度/尺寸單位。 1dp 在屏幕密度為 160dpi (基準密度) 的設備上等于 1px。系統會根據實際屏幕密度自動縮放。應始終用于指定 View 尺寸和邊距!
      • sp (Scale-independent Pixels): 字體大小單位。 類似 dp,但會額外尊重用戶系統的字體大小設置應始終用于字體大小!
      • 避免 px (Pixels): 直接對應屏幕物理像素,在不同密度屏幕上顯示大小不一致。
    • 布局適配:
      • 限定符: 使用 smallestWidth (sw<N>dp,如 sw600dp 用于 7 寸平板)、screen size (small, normal, large, xlarge - 已棄用,推薦 sw)、screen orientation (land 橫屏, port 豎屏) 為不同屏幕尺寸/方向提供不同的布局文件。
      • 響應式布局設計: 使用 ConstraintLayoutLinearLayout (權重 weight)、RelativeLayout 等構建能彈性伸縮和重新排列的布局。優先考慮 match_parent, wrap_content 和約束關系。
      • 使用 dimens.xml 為不同屏幕尺寸定義不同的尺寸值 (使用限定符目錄)。
  2. 語言/區域適配:

    • 限定符: 使用語言代碼 (en, zh)、區域代碼 (rUS, rCN) 創建不同的 values-<qualifier> 目錄 (如 values-en/, values-zh-rCN/)。
    • 存放內容: 在對應的 values-<qualifier>/strings.xml 等文件中放置翻譯好的字符串、本地化的圖片引用、日期/貨幣格式等。
    • 自動切換: 系統根據用戶設備的語言/區域設置,自動加載匹配的字符串資源。
  3. 夜間模式/主題適配:

    • 限定符: 使用 night (values-night/, drawable-night/)。
    • 主題屬性:styles.xml 中定義主題,使用主題屬性 (?attr/colorPrimary) 引用顏色等資源,而非硬編碼。在日間/夜間主題中為同一屬性指定不同的顏色值。
    • 動態切換: AppCompatDelegate.setDefaultNightMode() 允許 App 內動態切換日/夜模式。
  4. API 版本適配:

    • 限定符: 使用 v<N> (如 drawable-v21/) 提供只在特定 API 級別及以上可用的資源(如 Vector Drawables, 特定主題屬性)。
    • 代碼檢查: 在 Java/Kotlin 代碼中使用 Build.VERSION.SDK_INT 判斷系統版本,決定是否使用新 API 或提供兼容方案。

權限機制

核心目標: 保護用戶隱私和設備安全,防止 App 隨意訪問敏感數據(如位置、通訊錄、短信)或執行危險操作(如打電話、錄音、訪問外部存儲)。

核心原則: 最小權限原則 - App 只能獲取其明確聲明且用戶明確授權的權限。

一、 權限分類(按獲取時機與方式):

  1. 安裝時權限 (Install-Time Permissions / Normal Permissions):

    • 特點: 涉及低風險操作,對用戶隱私或設備操作影響極小。
    • 獲取方式: 在 App 安裝時,系統自動授予(用戶無需額外操作)。用戶無法在安裝后單獨撤銷這些權限。
    • 例子: 設置時區 (android.permission.SET_TIME_ZONE)、訪問網絡 (android.permission.INTERNET)、藍牙 (android.permission.BLUETOOTH)、振動 (android.permission.VIBRATE)。
  2. 運行時權限 (Runtime Permissions / Dangerous Permissions):

    • 特點: 涉及高風險操作,直接訪問用戶隱私數據或影響設備安全/其他 App 操作這是權限機制的核心和重點!
    • 獲取方式 (關鍵流程):
      1. 聲明:AndroidManifest.xml 中聲明需要的權限 (如 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>)。
      2. 檢查: 在代碼中執行需要該權限的操作之前,使用 ContextCompat.checkSelfPermission(Context, permissionString) 檢查該權限是否已被授予。
      3. 請求:
        • 如果未授予,調用 ActivityCompat.requestPermissions(Activity, new String[]{permissionString}, requestCode) 向用戶彈出系統對話框請求授權
        • 用戶可以選擇 允許拒絕
      4. 處理結果: 在 Activity/Fragment 中重寫 onRequestPermissionsResult(requestCode, permissions[], grantResults[]) 方法,處理用戶的授權選擇結果。
    • 關鍵點:
      • 用戶控制: 用戶可以在系統 設置 > 應用 > 權限 中隨時授予或撤銷這些權限。
      • 臨時拒絕 (Ask Every Time): 用戶首次拒絕時,系統可能會提供“僅此一次”或“使用時允許”的選項(取決于權限類型和系統版本)。如果用戶選擇了 拒絕 并且 勾選了 不再詢問 (或等效選項),后續請求將直接失敗。
      • 權限組: 運行時權限被分組管理(如 位置 組包含 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION)。一旦用戶授予了組內某個權限,再次請求組內其他權限時系統會自動授予(不會彈窗)。 但最佳實踐仍是顯式請求所需的所有權限。
    • 例子: 相機 (CAMERA)、位置 (ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)、通訊錄 (READ_CONTACTS)、麥克風 (RECORD_AUDIO)、短信 (SEND_SMS)、日歷 (READ_CALENDAR)、存儲 (READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE - 注意 Scoped Storage 限制)。
  3. 特殊權限 (Special Permissions):

    • 特點: 權限行為非常特殊,不在標準運行時權限流程內。通常涉及系統級設置或深度集成
    • 獲取方式: 無法通過 requestPermissions() 獲取! 需要引導用戶跳轉到特定的系統設置頁面 (Settings.ACTION_APPLICATION_DETAILS_SETTINGS 或其他特定 ACTION_..._SETTINGS) 去手動開啟。
    • 例子: 懸浮窗 (SYSTEM_ALERT_WINDOW)、修改系統設置 (WRITE_SETTINGS)、精確鬧鐘 (SCHEDULE_EXACT_ALARM - Android 12+)、電池優化忽略 (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)。
  4. 簽名權限 (Signature Permissions):

    • 特點: 主要用于系統 App 或由同一開發者簽名的 App 之間進行受保護的交互。
    • 獲取方式: 如果 App 的簽名證書與聲明該權限的 App/系統的簽名證書匹配,則系統會在安裝時自動授予。
    • 開發者控制: 普通開發者一般無法定義或使用新的簽名權限,主要用于平臺或預裝應用。

二、 關鍵機制與最佳實踐:

  1. AndroidManifest.xml 聲明是必須的: 任何權限(尤其是運行時權限)都必須先在清單文件中聲明,否則系統不會授予(即使代碼請求了)。
  2. 按需請求: 只在真正需要執行相關操作時才請求權限。避免在啟動時請求一堆權限(“權限轟炸”),這會讓用戶反感并卸載 App。
  3. 解釋為什么需要權限: 在請求權限前(尤其是用戶可能不理解為什么需要時),使用 ActivityCompat.shouldShowRequestPermissionRationale(Activity, permissionString) 檢查是否需要向用戶解釋。如果需要,先彈出自定義對話框解釋清楚、簡潔的原因,解釋完后再調用 requestPermissions()
  4. 優雅處理拒絕:
    • 如果用戶拒絕(未勾選“不再詢問”),可以在后續合適時機再次請求(并附帶解釋)。
    • 如果用戶永久拒絕(勾選“不再詢問”),應引導用戶到 App 的設置頁面 (Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 手動開啟權限,并禁用依賴該權限的功能(而不是崩潰或反復彈窗)。
  5. 權限組意識: 了解權限分組,但不要依賴自動授予行為作為不請求權限的理由。始終請求你需要的具體權限。
  6. 適配新版本: 關注新 Android 版本(如 11, 12, 13, 14)對權限模型的更新(如后臺位置訪問限制、照片選擇器、鄰近 Wi-Fi 權限、通知權限等),及時調整 App 邏輯。
  7. 存儲權限 (READ/WRITE_EXTERNAL_STORAGE) 的演變:
    • Android 10 (API 29) 引入 Scoped Storage: 限制 App 隨意訪問外部存儲其他 App 的私有文件。強調使用 MediaStore API 訪問媒體文件和 SAF (Storage Access Framework) 訪問特定文檔/目錄。
    • Android 11 (API 30) 及以后: 進一步收緊,MANAGE_EXTERNAL_STORAGE 成為特殊權限(需跳轉設置),普通 App 應盡量避免使用。優先使用 App 專屬目錄 (Context.getExternalFilesDir()) 和共享存儲 API (MediaStore, SAF)。

存儲機制

核心目標: 在保護用戶隱私和數據安全的前提下,為 App 提供可靠的文件存儲能力,并實現不同 App 之間的安全數據共享。

核心挑戰: 平衡 App 功能需求與用戶數據安全/隱私,尤其在設備文件系統日益復雜和惡意軟件威脅下。

一、 關鍵演變:從自由到嚴格(Scoped Storage 為核心)

  1. Android 10 (API 29) 之前:相對自由

    • WRITE_EXTERNAL_STORAGE 權限 = 萬能鑰匙: 一旦用戶授予,App 幾乎可以讀寫整個外部存儲(SD卡和內置存儲的公共部分)的任何文件,包括其他 App 的私有文件。隱私泄露風險高!
  2. Android 10 (API 29) 引入 Scoped Storage (分區存儲):重大變革!

    • 核心理念: 限制 App 隨意掃描整個存儲空間,保護用戶隱私和其他 App 的數據。
    • 關鍵變化:
      • 默認作用域: App 默認只能無需權限訪問:
        • 自身專屬的外部存儲目錄 (Context.getExternalFilesDir(), Context.getExternalCacheDir()):存放 App 私有文件,卸載時會被刪除。這是首選存放位置。
        • 特定類型的媒體文件 (圖片、視頻、音頻):但必須通過 MediaStore API 訪問(需要運行時權限 READ_EXTERNAL_STORAGE 來讀取其他 App 創建的媒體文件)。
      • WRITE_EXTERNAL_STORAGE 權限作用大幅縮減: 在 Android 10 上,它主要允許寫入 MediaStore不再能隨意寫任何地方!
      • 訪問其他 App 的私有目錄或非媒體文件: 必須使用 Storage Access Framework (SAF) (系統文件選擇器)。
  3. Android 11 (API 30) 及以后:強化與完善

    • 進一步限制: READ_EXTERNAL_STORAGE 權限也受到更嚴格限制。
    • MANAGE_EXTERNAL_STORAGE 成為特殊權限: 提供給文件管理器、備份恢復等需要廣泛文件訪問的特定類型 App。普通 App 強烈不建議申請,上架應用商店審核嚴格且用戶授權率極低。需要引導用戶跳轉到系統設置手動開啟。
    • 文件訪問意圖更明確:
      • 媒體文件: 優先且主要使用 MediaStore
      • 文檔/其他文件: 優先使用 Storage Access Framework (SAF)
      • App 自身文件: 使用 App 專屬目錄

通知機制

📣 核心流程(簡單版)

  1. APP想通知你: 某個應用(比如微信、郵箱、游戲)發生了需要你注意的事情(新消息、下載完成、系統提醒)。
  2. APP打包“通知”: APP按照安卓系統的規定,創建一個通知對象 (Notification)。這個對象包含:
    • 小圖標 (Small Icon): 在狀態欄顯示的小圖(必須)。
    • 標題 (Title): 通知的主題(比如“新消息”、“下載完成”)。
    • 內容文本 (Content Text): 通知的詳細內容(比如“張三:晚上吃飯嗎?”)。
    • 大圖標 (Large Icon - 可選): 展開通知后顯示的大圖(比如發信人頭像)。
    • 優先級 (Priority): 告訴系統這個通知有多緊急(高、中、低等,影響顯示位置和是否響鈴)。
    • 點擊動作 (PendingIntent): 最關鍵!你點擊通知后要做什么?(比如打開聊天窗口、跳轉到郵件詳情、播放音樂)。
    • 渠道 (Channel - Android 8.0+ 必須): 通知的分類(比如微信可以有“新消息”、“群通知”、“公眾號更新”等不同渠道)。用戶可以根據渠道單獨設置開關和提醒方式!
    • 其他花活 (可選): 進度條、按鈕(快速回復、標記已讀)、圖片、媒體控制等。
  3. APP把通知“遞”給系統: APP調用 NotificationManager.notify(id, notification) 方法,把這個打包好的通知對象交給安卓系統的 通知管理器 (Notification Manager)
  4. 系統“展示”通知:
    • 狀態欄圖標: 通知的小圖標會出現在屏幕頂部的狀態欄。
    • 通知抽屜: 下拉狀態欄,你會看到通知的詳細列表(標題、內容、圖標等)。
    • 提醒方式 (根據用戶設置):
      • 聲音 (Sound): 播放提示音。
      • 震動 (Vibrate): 手機震動。
      • 呼吸燈 (Lights - 如果手機有): 閃爍指示燈。
      • 浮動通知/彈窗 (Heads-up - 高優先級): 在屏幕頂部短暫彈出(不影響當前操作)。
    • 鎖屏顯示 (根據用戶設置): 通知內容可能顯示在鎖屏上(注意隱私)。

🔑 關鍵機制和規則

  1. 通知渠道 (Android 8.0 Oreo 引入):

    • 核心思想: 讓用戶精細控制通知! 不再是“整個APP的通知要么全開要么全關”。
    • APP的責任: APP必須為不同類型的通知創建不同的渠道 (Channel) (比如“交易提醒”、“營銷推送”、“聊天消息”)。
    • 用戶的權力: 用戶可以單獨為每個渠道設置:
      • 開關: 是否允許顯示。
      • 提醒方式: 是否響鈴、震動、浮動顯示、在鎖屏顯示。
      • 重要性 (Importance Level): 決定通知的干擾程度(緊急、高、中、低)。
    • 好處: 用戶能屏蔽煩人的廣告推送,但保留重要的聊天消息提醒。
  2. 通知權限:

    • Android 13 (Tiramisu) 之前: APP安裝后默認可以發通知。
    • Android 13 及以后: 新增運行時權限 POST_NOTIFICATIONS
      • 當APP第一次嘗試發通知時,系統會彈窗詢問用戶**“是否允許 [APP名稱] 發送通知?”**。
      • 用戶可以選擇 “允許”“不允許”
      • 開發者注意: 必須適配!用戶拒絕后,調用 notify() 會失效。
  3. 勿擾模式 (Do Not Disturb):

    • 用戶可以開啟“勿擾模式”(手動或按計劃)。
    • 在該模式下,只有被用戶標記為“允許打擾” 的APP或聯系人的通知(通常是最高優先級或特殊渠道)才會發出聲音/震動,其他通知會靜默進入通知抽屜。
  4. 后臺限制 (省電優化):

    • 安卓系統(尤其國產定制系統)對APP在后臺運行有嚴格限制,防止耗電。
    • 影響: 如果APP被系統“殺掉”或在后臺被嚴格限制,它可能無法及時觸發后臺服務來發送通知
    • 解決方案 (給開發者):
      • 使用 WorkManager 安排可靠的后臺任務(系統會找合適時機運行)。
      • 使用廠商推送服務 (如小米推送、華為推送、FCM) 替代APP自己維持長連接(更省電,推送更可靠)。
      • 引導用戶將APP加入“電池優化白名單”或“允許后臺運行”(效果因廠商而異)。
  5. 通知分組和摘要 (Android 7.0+):

    • 分組 (Grouping): 同一個APP的多個通知(比如多封未讀郵件)可以被折疊成一個“組”顯示,點擊組再展開詳情。避免通知欄被刷屏。
    • 摘要 (Bundling/Summary): 可以為分組提供一個摘要通知(比如“5條新消息”)。
  6. 長連接與推送服務:

    • APP主動拉取 (Polling): APP定期去服務器檢查新消息(耗電、不實時)。
    • 長連接 (Persistent Connection): APP在后臺和服務器保持一個連接,服務器有新消息可以立刻推給APP,APP再發通知(更實時,但APP需后臺保活,可能被系統限制)。
    • 統一推送服務 (FCM/廠商推送): 最佳實踐!
      • APP不需要自己維持長連接。
      • 服務器把通知消息發給 Google 的 Firebase Cloud Messaging (FCM)手機廠商的推送服務器 (如小米推送、華為推送)
      • FCM/廠商服務器利用系統級的、更省電的長連接通道,將消息推送到用戶設備
      • 設備系統收到后,直接喚醒目標APP或代表APP彈出通知(無需APP后臺運行)。
      • 好處: 省電、推送可靠、及時。

后臺執行限制

核心就是 “系統如何管住APP在后臺偷偷搞事情” 的規則,目的是 省電、省流量、保流暢、護隱私


🛑 核心目標:限制APP在后臺干啥?

系統想阻止APP在你不用它的時候:

  • 狂耗電: 后臺不斷聯網、定位、計算。
  • 偷跑流量: 后臺瘋狂上傳下載。
  • 拖慢手機: 后臺占用CPU和內存,讓你用前臺APP時卡頓。
  • 偷偷收集數據: 后臺掃描位置、讀取文件、監聽傳感器。

🔒 主要限制手段(不同安卓版本不斷加碼)

1. 后臺服務限制 (Android 8.0 Oreo 起關鍵變化)
  • 以前: APP可以輕松在后臺啟動一個Service(服務)長期運行(比如放音樂、下載文件、定時同步)。
  • 現在 (Android 8.0+):
    • 前臺服務 (Foreground Service): 如果APP需要在后臺做用戶可感知需要持續運行的任務(如音樂播放、導航、文件下載),必須啟動一個前臺服務!
      • 特點: 必須在狀態欄顯示一個常駐通知(告訴用戶“我正在后臺工作呢!”)。
      • 好處: 用戶知道誰在耗電,也能手動劃掉通知停止它。
    • 后臺服務 (Background Service):
      • APP在前臺或剛退到后臺: 可以正常啟動和使用后臺服務(有短暫寬限期)。
      • APP在后臺一段時間后: 系統會強制停止APP的所有后臺服務! APP想再啟動新服務?門都沒有!
  • 開發者應對: 需要長時間后臺任務?用前臺服務(配通知)!或者用更智能的調度方式(如WorkManager)。
2. 廣播接收器限制 (Android 8.0+)
  • 廣播 (Broadcast): 系統或APP發出的全局事件(比如開機完成、網絡變化、充電中)。
  • 以前: APP可以注冊監聽很多廣播(即使沒在運行),一收到廣播就能被喚醒干活。
  • 現在 (Android 8.0+):
    • 顯式廣播 (Explicit Broadcast): 發給特定APP的廣播,基本不受限。
    • 隱式廣播 (Implicit Broadcast): 發給所有APP的全局廣播(如 ACTION_BOOT_COMPLETED 開機完成、CONNECTIVITY_CHANGE 網絡變化)受到嚴格限制
      • 靜態注冊 (Manifest 里聲明): 大部分隱式廣播收不到了!只有少數系統白名單廣播例外(如開機完成,但應用首次啟動后也收不到了)。
      • 動態注冊 (代碼里注冊): APP在前臺時能收到,退到后臺后就收不到了
  • 目的: 防止一堆APP被無關緊要的全局廣播頻繁喚醒。
  • 開發者應對: 避免依賴隱式廣播喚醒后臺任務。用JobScheduler/WorkManager替代。
3. 后臺位置訪問限制 (Android 10+ 大幅收緊)
  • 以前: APP在后臺可以相對容易地獲取用戶位置。
  • 現在 (Android 10+):
    • 新增權限: ACCESS_BACKGROUND_LOCATION (后臺位置權限)。
    • 用戶授權更嚴格: 用戶必須在設置頁里單獨授予這個權限(不像前臺位置權限那樣在運行時彈窗就能給)。
    • 前臺服務要求: 即使有后臺位置權限,APP在后臺持續獲取位置信息時,也必須啟動一個前臺服務(并顯示通知告知用戶)。
  • 目的: 防止APP在后臺偷偷追蹤用戶位置,嚴重侵犯隱私。
  • 開發者應對: 非導航/運動類APP,強烈建議避免在后臺獲取位置。如必須,請求后臺權限并配前臺服務+通知。
4. 后臺網絡訪問限制 (Android 7.0+ Doze & App Standby)
  • Doze 模式 (打盹模式 - Android 6.0+):
    • 觸發: 手機滅屏、靜置、未充電一段時間后。
    • 限制:
      • 暫停所有后臺網絡訪問(WiFi和移動數據)。
      • 延遲所有后臺JobScheduler任務、SyncAdapter同步、AlarmManager鬧鐘(非精確鬧鐘)。
      • 禁止后臺服務啟動。
    • 維護窗口 (Maintenance Window): 系統會周期性地短暫退出Doze(例如每小時一次),讓被延遲的任務有機會執行。執行完又進入Doze。
  • App Standby (應用待機桶 - Android 6.0+):
    • 觸發: 用戶長時間沒用某個APP。
    • 限制: 將該APP放入限制桶 (Restricted Bucket)
      • 大幅限制后臺網絡訪問
      • 延遲后臺任務(JobScheduler/SyncAdapter)。
      • 禁止后臺服務啟動。
    • 用戶喚醒: 只要用戶手動啟動了該APP,它立刻跳出限制桶,恢復所有能力。
  • 目的: 限制不常用APP在后臺偷跑網絡和資源。
  • 開發者應對: 使用WorkManager調度網絡任務(它知道如何應對Doze和待機桶)。避免在后臺做不必要的網絡請求。
5. 廠商定制系統的“魔改” (尤其國內 ROM)
  • 更激進! 小米、華為、OPPO、vivo 等國產手機的系統,后臺限制往往比原生安卓更狠
  • 常見手段:
    • 自動啟動管理: 默認禁止APP開機自啟、被其他APP喚醒(鏈式啟動)。
    • 后臺運行管理: 鎖屏后幾分鐘就清理后臺APP進程和服務(即使你設置了前臺服務通知也可能被清!)。
    • 省電優化/電池管理: 用戶必須手動將APP加入“白名單”、“允許后臺運行”、“允許關聯啟動”、“忽略電池優化”,否則后臺任務幾乎無法運行。
    • 對齊喚醒: 強制所有APP的喚醒請求集中到某個時間點執行,減少頻繁喚醒。
  • 結果: 用戶省電效果可能更好,但開發者適配極其痛苦,后臺任務可靠性嚴重依賴用戶手動設置白名單

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

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

相關文章

MySQL進階:(第一篇) 深入解析MySQL存儲引擎架構

一、MySQL的體系結構連接層&#xff1a;最上層是一些客戶端和鏈接服務&#xff0c;主要完成一些類似于連接處理、授權認證、及相關的安全方案。服務器也會為安全接入的每個客戶端驗證它所具有的操作權限。服務層&#xff1a;第二層架構主要完成大多數的核心服務功能&#xff0c…

京東m端 滑塊 分析 t30

聲明: 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01;部分python代碼response requests.pos…

CentOS使用命令行工具為其配置靜態網絡并使用VMware軟件ovf配置文件快速配置多臺不同ip的centos文件

目錄 一、實驗前準備 1.SSH遠程登錄工具 二、CentOS配置靜態IP并實現遠程ssh登錄 1.VMware軟件查看NAT模式下默認網段和網關 2.使用ipconfig查看當前網卡名字和動態分配的ip地址 3.使用VIM編輯網絡配置文件&#xff08;此步驟可有其他編輯器替代&#xff0c;例如&#xf…

設計模式學習[17]---組合模式

文章目錄前言1.引例2.一致性抽象處理3.透明組合模式與安全組合模式總結前言 在畫類圖的時候&#xff0c;類與類之間有組合關系&#xff0c;聚合關系&#xff0c;我本來以為這個組合模式應該是整體與部分的關系&#xff0c;其實設計模式中的組合模式和類圖中的組合不是同一個東…

48Days-Day12 | 添加字符,數組變換,裝箱問題

添加字符 添加字符_牛客筆試題_牛客網 算法原理 因為本題數據量都比較小&#xff0c;所以我們可以直接使用暴力解法&#xff0c;枚舉B字符串的每一個位置作為與A字符串比較的起點&#xff0c;維護一個最小位數的值 代碼 import java.util.*;// 注意類名必須為 Main, 不要有…

關于npm前端項目編譯時棧溢出 Maximum call stack size exceeded的處理方案

背景&#xff1a;使用vueelementui的前端項目&#xff0c;使用jenkins進行自動化編譯部署&#xff0c;某天在進行編譯發版的時候&#xff0c;突然出現 npm ERR! Maximum call stack size exceeded 錯誤&#xff0c;一直都沒法編譯成功。原因&#xff1a;隨著前端項目的不斷迭代…

微信小程序組件發布為 npm 包的具體步驟

1. 準備工作 首先&#xff0c;您需要在系統上安裝 Node.js 和 npm。如果尚未安裝&#xff0c;請訪問 Node.js — Run JavaScript Everywhere 下載并安裝最新版本。 2. 創建獨立的組件目錄 為了更好地管理組件&#xff0c;建議將其從當前項目中獨立出來&#xff1a; wechat-…

LCM中間件入門(2):LCM核心實現原理解析

文章目錄一、good()函數&#xff1a;LCM實例狀態檢查的實現原理1. 實現邏輯2. 簡化代碼示例&#xff08;C語言核心邏輯&#xff09;二、publish()&#xff1a;向指定channel發送消息的原理1. 完整流程拆解2. 簡化代碼示例&#xff08;C核心邏輯&#xff09;三、subscribe()&…

Nginx安裝及配置

一.nginx安裝1.1nginx概述1.1.1 nginx介紹Nginx是一款高性能的開源HTTP和反向代理服務器&#xff0c;是免費的、開源的、高性能的HTTP和反向代理服務器、郵件代理服務器、以及TCP/UDP代理服務器解決C10K問題&#xff08;10K Connections&#xff09;。同時也支持IMAP/POP3代理服…

SelectDB數據庫,新一代實時數據倉庫的全面解析與應用

摘要&#xff1a;SelectDB是一款基于Apache Doris的新一代實時數據倉庫解決方案&#xff0c;具備實時極速、融合統一、彈性架構和開放生態四大核心特性。它采用云原生存算分離架構&#xff0c;支持秒級數據更新、毫秒級查詢響應&#xff0c;在TPC-H等基準測試中性能超越傳統系統…

自動駕駛的未來:多模態傳感器鉆機

倫敦大學學院博士生袁方正在建造多模態傳感器鉆機&#xff0c;以探索自動駕駛的未來。他的最新設置匯集了一套尖端傳感器&#xff1a; &#x1f4e1; 60 GHz 雷達&#xff08;用于 Raspberry Pi 的 DreamHAT&#xff09;DreamRF &#x1f4f7; RGB 深度攝像頭 &#xff08;Real…

13.Redis 的級聯復制

Redis 的級聯復制 即實現基于Slave節點的Slave 1. 修改 Slave 節點配置文件 # 第一個slave節點 [rootubuntu2204 ~]#vim /apps/redis/etc/redis.conf(大約在533行附近) replicaof 10.0.0.100 6379 masterauth 123456# 第二個slave節點 [rootubuntu2204 ~]#vim /apps/redis/etc/…

spring-ai-alibaba 學習(二十)——graph之檢查點

前面學習了graph的基本概念&#xff0c;參數設置&#xff0c;特殊節點和邊&#xff0c;今天學習一下檢查點檢查點可能名稱比較抽象&#xff0c;換個名字可能比較容易理解&#xff0c;進度保存點或者存檔點&#xff0c;可以類比游戲中保存當前游戲進度的存檔進度主要用于人工介入…

sqli-labs:Less-19關卡詳細解析

1. 思路&#x1f680; 本關的SQL語句為&#xff1a; $insert"INSERT INTO security.referers (referer, ip_address) VALUES ($uagent, $IP)";注入類型&#xff1a;字符串型&#xff08;單引號包裹&#xff09;、INSERT操作提示&#xff1a;參數需以閉合關鍵參數&a…

Java小紅書源碼1:1還原uniapp_仿小紅書源碼

在內容驅動型社交平臺興起的背景下&#xff0c;小紅書作為圖文/視頻種草社區的代表&#xff0c;其產品結構與功能體驗逐漸成為眾多開發者與創業團隊的模仿藍本。本項目基于Java后端uni-app前端棧&#xff0c;完整復刻小紅書主要功能&#xff0c;支持多端&#xff08;小程序、H5…

USB Type-C PD協議一文通

原文&#xff1a;https://www.richtek.com/Design%20Support/Technical%20Document/AN056?sc_langzh-TW譯者&#xff1a;TrustZone1、概述 USB Type-C標準的出現是為了滿足不斷增長的現代設備之間的連接需要&#xff0c;它在傳統USB標準的基礎上提供了更高的電源傳輸能力和資料…

AI文檔比對和Word的“比較”功能有什么區別?

AI文檔比對工具的核心區別在于&#xff0c;它超越了Word的純文本“找不同”&#xff0c;能精準處理掃描件、表格及印章&#xff0c;并將文檔審查從被動的文本核對&#xff0c;處理大文檔也更為快速及準確。 為什么Word的“比較”功能已經不夠用了&#xff1f; 對于許多專業人士…

AI驅動SEO關鍵詞智能進化

內容概要 隨著人工智能&#xff08;AI&#xff09;技術的快速演進&#xff0c;搜索引擎優化&#xff08;SEO&#xff09;領域正迎來前所未有的變革。本文核心探討AI如何驅動SEO關鍵詞的智能進化&#xff0c;重點解析人工智能革新關鍵詞研究與優化策略的機制&#xff0c;包括智能…

基于SpringBoot+MyBatis+MySQL+VUE實現的青年公寓服務平臺管理系統(附源碼+數據庫+畢業論文+部署教程+配套軟件)

摘 要 傳統信息的管理大部分依賴于管理人員的手工登記與管理&#xff0c;然而&#xff0c;隨著近些年信息技術的迅猛發展&#xff0c;讓許多比較老套的信息管理模式進行了更新迭代&#xff0c;房屋信息因為其管理內容繁雜&#xff0c;管理數量繁多導致手工進行處理不能滿足廣…

12.Redis 主從復制

Redis 主從復制Redis 主從復制1. Redis 主從復制架構2. 主從復制實現2.1 主從命令配置2.1.1 啟用主從同步2.1.2 查看日志觀察同步狀態2.1.3 修改 Slave 節點配置文件2.1.4 刪除主從同步3. 主從復制故障恢復3.1 Slave 節點故障和恢復3.2 Master 節點故障和恢復3.3 常見主從復制故…