一、APK安裝原理
二、APP啟動(dòng)流程優(yōu)化
三、Android App 的安裝過(guò)程
在分析安裝過(guò)程之前,需要先了解一下 Android 項(xiàng)目是如何經(jīng)過(guò)編譯->打包生成最終的?.apk?格式的安裝包颅崩。谷歌有一張官方圖片來(lái)描述?apk?的打包流程,如下圖所示蕊苗。
一個(gè)完整的 Android 項(xiàng)目可能包含多個(gè)?module沿后,而從宏觀上看每一個(gè)?module?中的內(nèi)容可以分為?2?部分:Resources?資源文件、Java?或者?Kotlin?源代碼岁歉。因此整個(gè)項(xiàng)目的編譯打包過(guò)程也是針對(duì)這?2?部分來(lái)完成得运。
編譯階段
Resources?資源文件
資源文件包括項(xiàng)目中?res?目錄下的各種?XML?文件膝蜈、動(dòng)畫(huà)、drawable?圖片熔掺、音視頻等饱搏。AAPT?工具負(fù)責(zé)編譯項(xiàng)目中的這些資源文件,所有資源文件會(huì)被編譯處理置逻,XML?文件(drawable?圖片除外)會(huì)被編譯成二進(jìn)制文件推沸,所以解壓?apk?之后無(wú)法直接打開(kāi)?XML?文件。但是?assets?和?raw?目錄下的資源并不會(huì)被編譯券坞,會(huì)被原封不動(dòng)的打包到?apk?壓縮包中鬓催。
資源文件編譯之后的產(chǎn)物包括兩部分:resources.arsc?文件和一個(gè)?R.java。前者保存的是一個(gè)資源索引表恨锚,后者定義了各個(gè)資源?ID?常量宇驾。這兩者結(jié)合就可以在代碼中找到對(duì)應(yīng)的資源引用
源碼部分
項(xiàng)目中的源代碼首先會(huì)通過(guò)?javac?編譯為?.class?字節(jié)碼文件,然后這些?.class?文件連同依賴(lài)的三方庫(kù)中的?.class?文件一同被?dx?工具優(yōu)化為?.dex?文件猴伶。如果有分包课舍,那么也可能會(huì)生成多個(gè)?.dex?文件。
實(shí)際上源代碼文件也包括?AIDL?接口文件編譯之后生成的?.java?文件他挎,Android?項(xiàng)目中如果包含?.aidl?接口文件筝尾,這些?.aidl?文件會(huì)被編譯成?.java?文件。
打包階段
最后使用工具?APK Builder?將經(jīng)過(guò)編譯之后的?resource?和?.dex?文件一起打包到?apk?中办桨,實(shí)際上被打包到?apk?中的還有一些其他資源筹淫,比如?AndroidManifest.xml?清單文件和三方庫(kù)中使用的動(dòng)態(tài)庫(kù)?.so?文件。
apk?創(chuàng)建好之后呢撞,還不能直接使用损姜。需要使用工具?jarsigner?對(duì)其進(jìn)行簽名,因?yàn)?Android?系統(tǒng)不會(huì)安裝沒(méi)有進(jìn)行簽名的程序殊霞。簽名之后會(huì)生成?META_INF?文件夾薛匪,此文件夾中保存著跟簽名相關(guān)的各個(gè)文件。
CERT.SF:生成每個(gè)文件相對(duì)的密鑰
MANIFEST.MF:數(shù)字簽名信息
xxx.SF:這是?JAR?文件的簽名文件
xxx.DSA:對(duì)輸出文件的簽名和公鑰脓鹃。
但是實(shí)際打包過(guò)程還會(huì)多一步?apk?優(yōu)化操作。就是使用工具?zipalign?對(duì)?apk?中的未壓縮資源(圖片古沥、視頻等)進(jìn)行對(duì)齊操作瘸右,讓資源按照?4?字節(jié)的邊界進(jìn)行對(duì)齊。這種思想同?Java?對(duì)象內(nèi)存布局中的對(duì)齊空間非常類(lèi)似岩齿,主要是為了加快資源的訪問(wèn)速度太颤。如果每個(gè)資源的開(kāi)始位置都是上一個(gè)資源之后的?4n?字節(jié),那么訪問(wèn)下一個(gè)資源就不用遍歷盹沈,直接跳到?4n?字節(jié)處判斷是不是一個(gè)新的資源即可龄章。
至此一個(gè)完整的?apk?安裝包就創(chuàng)建成功吃谣,一個(gè)完整的?apk?解壓縮之后的內(nèi)容如下所示:
PMS安裝過(guò)程概覽
當(dāng)我們點(diǎn)擊某一個(gè)?App 安裝包進(jìn)行安裝時(shí),首先會(huì)彈出一個(gè)系統(tǒng)界面指示我們進(jìn)行安裝操作做裙。這個(gè)界面是 Android Framework 中預(yù)置的一個(gè) Activity—PackageInstallerActivity.java岗憋。當(dāng)點(diǎn)擊安裝后,PackageInstallerActivity 最終會(huì)將所安裝的 apk 信息通過(guò) PackageInstallerSession 傳給 PMS锚贱,具體方法在 commitLocked 方法中仔戈,
圖中的?mPm 就是系統(tǒng)服務(wù) PackageManagerService。installStage 方法就是正式開(kāi)始 apk 的安裝過(guò)程拧廊。
整個(gè)?apk 的安裝過(guò)程可以分為兩大步:
拷貝安裝包监徘;
裝載代碼。
拷貝安裝包
從?installStage 方法開(kāi)始看起
圖中?1?處創(chuàng)建了類(lèi)型為?INIT_COPY?的?Message吧碾。
圖中?2?處創(chuàng)建?InstallParams凰盔,并傳入安裝包的相關(guān)數(shù)據(jù)。
Message?發(fā)送出去之后倦春,由?PMS?的內(nèi)部類(lèi)?PackageHandler?接收并處理户敬,
可以看出在?copyApk?方法中調(diào)用了?doCopyApk?方法,doCopyAPk?方法中主要做了?3?件事情:
圖中?1?處創(chuàng)建存儲(chǔ)安裝包的目標(biāo)路徑溅漾,實(shí)際上是?/data/app/?應(yīng)用包名目錄山叮;
圖中?2?處調(diào)用服務(wù)的?copyPackage?方法將安裝包?apk?拷貝到目標(biāo)路徑中;
圖中?3?處將?apk?中的動(dòng)態(tài)庫(kù)?.so?文件也拷貝到目標(biāo)路徑中添履。
裝載代碼
圖中?1?處執(zhí)行預(yù)安裝操作屁倔,主要是檢查安裝包的狀態(tài),確保安裝環(huán)境正常暮胧,如果安裝環(huán)境有問(wèn)題會(huì)清理拷貝文件锐借。
圖中?2?處是真正的安裝階段,installPackageTraceLI?方法中添加跟蹤?Trace往衷,然后調(diào)用?installPackageLI?方法進(jìn)行安裝钞翔。
圖中?3?處處理安裝完成之后的操作。
圖中?1?處調(diào)用?PackageParser?的?parsePackage?方法解析?apk?文件席舍,主要是解析?AndroidManifest.xml?文件布轿,將結(jié)果記錄在?PackageParser.Package?中。我們?cè)谇鍐挝募新暶鞯?Activity来颤、Service?等組件就是在這一步中被記錄到系統(tǒng)?Framework?中汰扭,后續(xù)才可以通過(guò)?startActivity?或者?startService?啟動(dòng)相應(yīng)的活動(dòng)或者服務(wù)。
圖中?2?處對(duì)?apk?中的簽名信息進(jìn)行驗(yàn)證操作福铅。collectCertificates?做簽名驗(yàn)證萝毛,collectManifestDigest?主要是做包的項(xiàng)目清單摘要的收集,主要適合用來(lái)比較兩個(gè)包的是否一樣滑黔。如果我們?cè)O(shè)備上已經(jīng)安裝了一個(gè)?debug?版本的?apk笆包,再次使用一個(gè)?release?版本的?apk?進(jìn)行覆蓋安裝時(shí)环揽,會(huì)在這一步驗(yàn)證失敗,最終導(dǎo)致安裝失敗庵佣。
圖中?3?處時(shí)執(zhí)行?dex?優(yōu)化歉胶,實(shí)際為?dex2oat?操作,用來(lái)將?apk?中的?dex?文件轉(zhuǎn)換為?oat?文件秧了。
圖中?4?處調(diào)用?installNewPackageLI?方法執(zhí)行新?apk?的安裝操作
installNewPackageLI?方法負(fù)責(zé)完成最后的?apk?安裝過(guò)程跨扮,具體代碼如下:
解釋說(shuō)明:
scanPackageLI?繼續(xù)掃描解析?apk?安裝包文件,保存?apk?相關(guān)信息到?PMS?中验毡,并創(chuàng)建?apk?的?data?目錄衡创,具體路徑為?/data/data/應(yīng)用包名。
updateSettingsLI?如果安裝成功晶通,更新系統(tǒng)設(shè)置中的應(yīng)用信息璃氢,比如應(yīng)用的權(quán)限信息。
deletePackageLI?如果安裝失敗狮辽,則將安裝包以及各種緩存文件刪除
至此整個(gè)?apk?的安裝過(guò)程結(jié)束一也,實(shí)際上安裝成功之后,還會(huì)發(fā)送一個(gè)?App?安裝成功的廣播?ACTION_PACKAGE_ADDED喉脖。手機(jī)桌面應(yīng)用注冊(cè)了這個(gè)廣播椰苟,當(dāng)接收到應(yīng)用安裝成功之后,就將?apk?的啟動(dòng)?icon?顯示在桌面上树叽。
總結(jié)
這節(jié)課主要介紹了一個(gè)?Android?項(xiàng)目從編譯成?apk?文件舆蝴,然后被安裝到手機(jī)設(shè)備上的簡(jiǎn)要過(guò)程。其中編譯分?2?塊內(nèi)容:資源?+?源代碼题诵。并且生成?apk?之后還要經(jīng)過(guò)簽名洁仗、對(duì)齊等操作。apk?安裝也分?2?塊進(jìn)行:安裝包拷貝和代碼裝載性锭。
可以關(guān)注我的公眾號(hào):Android架構(gòu)師成長(zhǎng)之路