Android APK 安裝與 Activity 啟動機(jī)制詳解

一、APK 安裝流程詳解

1.1 安裝步驟全解析

APK 的安裝可總結(jié)為以下 7 個(gè)關(guān)鍵步驟,無論 APK 是通過 adb 安裝(存儲于 PC 磁盤)還是通過應(yīng)用市場安裝(存儲于設(shè)備),流程一致:

步驟 1:臨時(shí)拷貝

  • APK 首先被拷貝到 /data/app/xxx.tmp 目錄下;
  • 其中 xxx 是一個(gè)隨機(jī)生成的字符串。

步驟 2:驗(yàn)證與重命名

  • 經(jīng)過重重驗(yàn)證與校驗(yàn)(包括簽名、版本號等);
  • /data/app/xxx.tmp 目錄會被重命名為:
    /data/app/[randomStrA]/[packageName]-[randomStrB]
    
  • 被拷貝的 APK 最終路徑為:
    /data/app/[randomStrA]/[packageName]-[randomStrB]/base.apk
    
  • 同時(shí)會為 APK 生成一個(gè)唯一的 ID,又稱 appid。

步驟 3:Manifest 解析與 PMS 注冊

  • 解析 APK 的 AndroidManifest.xml 中的內(nèi)容為 ParsedPackage;
  • ParsedPackage 中的權(quán)限等信息經(jīng)過驗(yàn)證通過后,傳遞給 PMS(PackageManagerService);
  • 這樣其他系統(tǒng)服務(wù)(如 ActivityManagerService)就可以從 PMS 獲取剛安裝 APK 的信息。

步驟 4:持久化安裝信息

  • 剛安裝的 APK 的安裝信息(如包名、版本、簽名證書、安裝時(shí)間等)會存儲到 PackageSetting;
  • PackageSetting 會傳遞給 Settings;
  • Settings 會將其持久化到 packages.xml 文件中。

步驟 5:創(chuàng)建 App Data 根目錄

  • 創(chuàng)建 app data 根目錄,該目錄是 APK 運(yùn)行期間數(shù)據(jù)存儲的根目錄;
  • 權(quán)限控制:只有當(dāng)前 APK 程序擁有該目錄的讀、寫、執(zhí)行權(quán)限,其他應(yīng)用沒有任何權(quán)限

步驟 6:DEX 優(yōu)化(可選但推薦)

  • 對 APK 的 DEX 文件進(jìn)行優(yōu)化(如生成 OAT 文件);
  • 注意:優(yōu)化即使不成功也不影響 APK 的安裝;
  • DEX 優(yōu)化可顯著提升 App 運(yùn)行性能。

步驟 7:發(fā)送安裝成功廣播

  • 系統(tǒng)發(fā)送 ACTION_PACKAGE_ADDED 等安裝成功廣播,通知其他組件。

【補(bǔ)充】
安裝耗時(shí)主要取決于 APK 大小和內(nèi)容復(fù)雜度。APK 越大、包含的 .so 庫越多,安裝時(shí)間越長。主要耗時(shí)體現(xiàn)在:

  • 拷貝 APK 文件;
  • 提取 native libraries(.so 文件);
  • DEX 優(yōu)化(如 ART 編譯)。

1.2 參考資料與圖示

系統(tǒng)啟動流程
Android啟動流程
啟動流程圖
啟動流程圖
Framework源碼解析

【補(bǔ)充】
上述圖片雖標(biāo)題為“啟動流程”,但實(shí)際展示了 Android 系統(tǒng)從 Zygote 到 AMS/PMS 初始化的完整鏈路,與 APK 安裝密切相關(guān),因安裝過程依賴 PMS 和 Installer 服務(wù)。


二、Activity 啟動流程(Framework 層調(diào)用鏈)

2.1 啟動調(diào)用棧(自上而下)

Activity#startActivity
Activity#startActivityForResult
Instrumentation#execStartActivity()
ActivityManagerService#startActivity()
ActivityTaskManagerService#startActivity()
ActivityTaskManagerService#startActivityAsUser()
ActivityStarter#execute()
ActivityStarter#startActivityMayWait()
ActivityStarter#startActivity()
ActivityStarter#startActivityUnchecked()
RootActivityContainer#startActivityLocked()
ActivityStack#resumeTopActivityUncheckedLocked()
ActivityStack#resumeTopActivityInnerLocked()

【補(bǔ)充】
自 Android 10 起,Activity 管理職責(zé)從 ActivityManagerService(AMS)部分遷移到新的 ActivityTaskManagerService(ATMS),以支持更復(fù)雜的多窗口和分屏場景。


三、Task 與回退棧機(jī)制

3.1 Task 的基本概念

  • 當(dāng)用戶點(diǎn)擊桌面上的 App 圖標(biāo)時(shí),App 的默認(rèn) Activity(即配置了 MAIN + LAUNCHER intent-filter 的 Activity)會被啟動;
  • 該 Activity 會被放入系統(tǒng)剛創(chuàng)建的一個(gè) Task 中。

3.2 最近任務(wù)切換的本質(zhì)

  • 用戶通過“最近任務(wù)鍵”在多個(gè) App 之間切換,實(shí)質(zhì)是在多個(gè) Task 之間切換;
  • 每個(gè) Task 都有一個(gè)自己的回退棧(back stack),按順序記錄用戶打開的每個(gè) Activity;
  • 按返回鍵時(shí),系統(tǒng)按倒序依次關(guān)閉這些 Activity。

3.3 Task 的生命周期與“殘影”

  • 當(dāng)回退棧中最后一個(gè) Activity 被關(guān)閉,Task 的生命結(jié)束
  • 但該 Task 不會立即從最近任務(wù)列表消失;
  • 系統(tǒng)保留一個(gè)“殘影”供用戶“切回去”;
  • 此時(shí)“切回去”實(shí)為重新啟動 App,因?yàn)樵?Task 已銷毀;
  • 結(jié)論:在最近任務(wù)里看見的 Task,未必還活著。

3.4 Task 的疊加行為

  • 不僅 Activity 可在 Task 內(nèi)部疊成棧,不同的 Task 之間也可以疊加
  • 限制:Task 的疊加只適用于前臺 Task;
  • 一旦前臺 Task 進(jìn)入后臺(如按 Home 鍵或最近任務(wù)鍵),疊加的多個(gè) Task 會立即被拆開。

四、Activity LaunchMode 詳解

4.1 Standard(默認(rèn)模式)

  • 每次啟動都會創(chuàng)建新實(shí)例,并加入當(dāng)前 Task 棧頂。

4.2 SingleTop

  • 行為與 Standard 類似,唯一區(qū)別
    • 如果棧頂?shù)?Activity 恰好是要啟動的 Activity,則不新建,而是調(diào)用其 onNewIntent() 方法。

4.3 SingleTask

  • 任務(wù)棧中唯一
    • 啟動同一個(gè) Activity 時(shí),會清除其上方的所有 Activity;
    • 常用于應(yīng)用首頁。
  • 全局唯一性保證
    • 保證“只有一個(gè) Task 里有這個(gè) Activity”;
    • 且“這個(gè) Task 里最多只有一個(gè)這個(gè) Activity”;
    • 因此,盡管名為 singleTask,實(shí)質(zhì)上限制了該 Activity 在全局只有一個(gè)對象。
  • 跨 App 行為
    • 當(dāng)被其他 App 啟動時(shí),不會進(jìn)入啟動者的 Task;
    • 而是在屬于它自己的 Task 中創(chuàng)建,并置于棧頂;
    • 然后將整個(gè) Task 被系統(tǒng)標(biāo)記為處于前臺,也可以理解為壓在啟動者 Task 之上。

4.4 SingleInstance

  • 系統(tǒng)唯一
    • 整個(gè)系統(tǒng)只有一個(gè)任務(wù)棧,且只有一個(gè) Activity 實(shí)例;
    • 常用于來電顯示等全局獨(dú)占場景。
  • 啟動行為
    • 啟動新 Activity 時(shí),由于 singleInstance 限制,新 Activity 不會進(jìn)入當(dāng)前 Task;
    • 而是被放入另一個(gè)獨(dú)立的 Task,并隨該 Task 一起被系統(tǒng)標(biāo)記為處于前臺。
  • 返回鍵行為差異(vs SingleTask):
    • singleInstance:按返回鍵會直接回到原先的 App;
    • singleTask:在自己的 App 內(nèi)回退。
  • 桌面啟動行為差異
    • singleInstance:從桌面再次點(diǎn)開 App 時(shí),看不到該 Activity(它仍在后臺某個(gè) Task 中);
      • 并未被殺死,再次啟動會回到前臺,并收到 onNewIntent() 回調(diào)。
    • standard/singleTop:Activity 依然在棧頂可見。

【補(bǔ)充】
singleInstance 的 Task 不會出現(xiàn)在最近任務(wù)列表中(或被同 affinity 的其他 Task 排擠),因此用戶“看不見”它,但它依然存活。


五、taskAffinity 機(jī)制

5.1 默認(rèn)行為

  • 一個(gè) App 默認(rèn)只能有一個(gè) Task 顯示在最近任務(wù)列表里;
  • 用于甄別“唯一性”的并非 App 本身,而是 taskAffinity

5.2 taskAffinity 的取值規(guī)則

  • 默認(rèn)取自 ApplicationtaskAffinity
  • ApplicationtaskAffinity 默認(rèn)為 App 的包名。

5.3 Task 的 taskAffinity

  • 每個(gè) Task 的 taskAffinity 取自其棧底 ActivitytaskAffinity。

5.4 singleTask 與 taskAffinity 的交互

  • 若新 Activity 配置為 singleTask,Android 會檢查:
    • 新 Activity 的 taskAffinity 與當(dāng)前 Task 的 taskAffinity 是否相同;
    • 相同 → 繼續(xù)入棧;
    • 不同 → 進(jìn)入與其 taskAffinity 相同的現(xiàn)有 Task,或創(chuàng)建新 Task。

5.5 最近任務(wù)顯示規(guī)則

  • 不同的 Task 會并列展示在最近任務(wù)列表中;
  • 前提:它們的 taskAffinity 必須不同;
  • 同一個(gè) taskAffinity 可創(chuàng)建多個(gè) Task,但最多只有一個(gè)能顯示在最近任務(wù)列表;
  • 這解釋了為何 singleInstance Activity 會從最近任務(wù)中“消失”——被同 affinity 的其他 Task 搶占。
  • 不同 Task 之間:是 “并行存在于系統(tǒng)任務(wù)列表” 的關(guān)系,而非 “堆疊”,系統(tǒng)會記錄哪個(gè) Task 處于前臺。

六、allowTaskReparenting 機(jī)制(Task Reparenting)

6.1 默認(rèn)行為限制

  • Activity 默認(rèn)只歸屬于一個(gè) Task,不會在多個(gè) Task 間跳轉(zhuǎn)。

6.2 啟用 allowTaskReparenting

  • 若將 Activity 的 android:allowTaskReparenting="true"
    • 當(dāng)從短信 App 打開郵件編寫 Activity 時(shí),它會進(jìn)入短信 App 的 Task
    • 但當(dāng)用戶稍后從桌面打開郵件 App 時(shí),該 Activity 會被自動挪回郵件 App 的 Task,置于棧頂;
    • 切回短信 App 時(shí),該 Activity 已消失。

6.3 與 singleTask 的對比優(yōu)勢

  • 無感知切換:沒有 Task 切換的夸張入場動畫;
  • 回退路徑連續(xù):用戶切后臺再回來,不會像 singleTask 那樣被切斷回退路徑。

6.4 兼容性問題

  • Android 9 和 10 上該屬性失效
  • Android 11 上被修復(fù),恢復(fù)正常行為。

七、LaunchMode 使用場景總結(jié)

模式 主要用途
standard / singleTop App 內(nèi)部常規(guī)跳轉(zhuǎn)
singleInstance 開放給其他 App 使用的共享 Activity(如來電、支付頁)
singleTask 兼容派:既用于內(nèi)部(如首頁),也用于外部共享

【核心理解】
理解 launchMode 需從多應(yīng)用間的 Activity 跳轉(zhuǎn)入手。在一個(gè)應(yīng)用內(nèi)和多個(gè)應(yīng)用間,它們表現(xiàn)不同,但本質(zhì)統(tǒng)一:

  • Task 的唯一標(biāo)識是 affinity
  • affinity 取決于棧底 Activity;
  • 系統(tǒng)通過標(biāo)記最近前臺應(yīng)用來處理Task的切換和回退,從感官上實(shí)現(xiàn)Task 內(nèi)部可以疊加 Task,但一旦進(jìn)入后臺會拆開;
  • Task 間的切換會有過場動畫。

八、擴(kuò)展參考資料(原樣保留)


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容