1. 程式人生 > >apk從打包到安裝到啟動

apk從打包到安裝到啟動

apk從打包安裝啟動

參考學習:

打包:

安裝:

應用啟動:

apk打包

流程概述:

1、打包資原始檔,生成R.java檔案

2、處理aidl檔案,生成相應java 檔案

3、編譯工程原始碼,生成相應class檔案

4、轉換所有class檔案,生成classes.dex檔案

5、打包生成apk

6、對apk檔案進行簽名

7、對簽名後的apk檔案進行對其處理

具體流程



第一步:打包資原始檔,生成R.java檔案。

【輸入】Resource檔案(就是工程中res中的檔案)、Assets檔案(相當於另外一種資源,這種資源Android系統並不像對

res中的檔案那樣優化它)、AndroidManifest.xml檔案(包名就是從這裡讀取的,因為生成R.java檔案需要包名)、Android基礎類庫(Android.jar檔案)

【工具】aapt工具(AndroidAsset Packaging Tool

【輸出】打包好的資源(bin目錄中的resources.ap_檔案)、R.java檔案(gen目錄中)

打包資源的工具aapt大部分文字格式的XML資原始檔會被編譯成二進位制格式的XML資原始檔,除了assetsres/raw資源被原裝不動地打包進APK之外,其它的資源都會被編譯或者處理。

生成過程主要是1呼叫了aapt原始碼目錄下的

Resource.cpp檔案中的buildResource()函式,該函式首先1.1檢查AndroidManifest.xml的合法性,然後1.2res目錄下的資源子目錄進行處理,處理的函式為makeFileResource(),處理的內容包括1.2.1資原始檔名的合法性檢查,1.2.2向資源表table新增條目等,處理完後呼叫compileResourceFile()函式1.3編譯resasserts目錄下的資源並生成resources.arsc檔案,compileResourceFile()函式位於aapt原始碼目錄的ResourceTable.cpp檔案中,該函式最後會呼叫
parseAndAddEntry()函式1.4生成R.java檔案,完成資源編譯後,接下來呼叫compileXmlfile()函式2res目錄的子目錄下的xml檔案分別進行編譯,這樣處理過的2.1xml檔案就簡單的被加密了,最後將所有的資源與編譯生成的resorces.arsc檔案以及加密過的AndroidManifest.xml檔案2.2打包壓縮成resources.ap_檔案(使用Ant工具命令列編譯則會生成與build.xml“project name”指定的屬性同名的ap_檔案)。

第二步:處理aidl檔案,生成相應的java檔案。

【輸入】原始碼檔案、aidl檔案、framework.aidl檔案

【工具】aidl工具

【輸出】對應的.java檔案

對於沒有使用到aidlandroid工程,這一步可以跳過。aidl工具解析介面定義檔案並生成相應的java程式碼供程式呼叫。

第三步:編譯工程原始碼,生成下相應的class檔案。

【輸入】原始碼檔案(包括R.javaAIDL生成的.java檔案)、庫檔案(.jar檔案)

【工具】javac工具

【輸出】.class檔案

這一步呼叫了javac編譯工程src目錄下所有的java原始檔,生成的class檔案位於工程的bin\classes目錄下,上圖假定編譯工程原始碼時程式是基於android SDK開發的,實際開發過程中,也有可能會使用android NDK來編譯native程式碼,因此,如果可能的話,這一步還需要使用android NDK編譯C/C++程式碼,當然,編譯C/C++程式碼的步驟也可以提前到第一步或第二步。

第四步:轉換所有的class檔案,生成classes.dex檔案。

【輸入】 .class檔案(包括Aidl生成.class檔案,R生成的.class檔案,原始檔生成的.class檔案),庫檔案(.jar檔案)

【工具】javac工具

【輸出】.dex檔案

前面多次提到,android系統dalvik虛擬機器的可執行檔案為dex格式,程式執行所需的classes.dex檔案就是在這一步生成的,使用的工具為dxdx工具主要的工作是將java位元組碼轉換為dalvik位元組碼、壓縮常量池、消除冗餘資訊等。

第五步:打包生成apk。

【輸入】打包後的資原始檔、打包後類檔案(.dex檔案)、libs檔案(包括.so檔案,當然很多工程都沒有這樣的檔案,如果你不使用C/C++開發的話)

【工具】apkbuilder工具

【輸出】未簽名的.apk檔案

打包工具為apkbuilderapkbuilder為一個指令碼檔案,實際呼叫的是android-sdk\tools\lib\sdklib.jar檔案中的com.android.sdklib.build.ApkBuilderMain類。它的程式碼實現位於android系統原始碼的sdk\sdkmanager\libs\sdklib\src\com\android\sdklib\build\ApkBuilderMain.java檔案,程式碼1構建了一個ApkBuilder類,然後2以包含resources.arsc的檔案為基礎生成apk檔案,這個檔案一般為ap_結尾,接著呼叫addSourceFolder()函式3新增工程資源,addSourceFolder()會呼叫processFileForResource()函式往apk檔案中新增資源,處理的內容包括res目錄與asserts目錄中的檔案,新增完資源後呼叫addResourceFromJar()4函式往apk檔案中寫入依賴庫,接著呼叫addNativeLibraries()函式5新增工程libs目錄下的Native庫(通過android NDK編譯生成的sobin檔案),6最後呼叫sealApk()關閉apk檔案。

第六步:對apk檔案進行簽名。

【輸入】未簽名的.apk檔案

【工具】jarsigner

【輸出】簽名的.apk檔案

android的應用程式需要簽名才能在android裝置上安裝,簽名apk檔案有兩種情況:一種是在除錯程式時進行簽名,使用eclipse開發android程式時,在編譯除錯程式時會自己使用一個debug.keystoreapk進行簽名;另一種是打包釋出時對程式進行簽名,這種情況下需要提供一個符合android開發文件中要求的簽名檔案。簽名的方法也分兩種:一種是使用jdk中提供的jarsigner工具簽名;另一種是使用android原始碼中提供的signapk工具,它的程式碼位於android系統原始碼build\tools\signapk目錄下。

第七步:對簽名後的apk檔案進行對齊處理。

【輸入】簽名後的.apk檔案

【工具】zipalign工具

【輸出】對齊後的.apk檔案

這一步需要使用的工具為zipalign,它位於android-sdk\tools目錄,原始碼位於android系統原始碼的build\tools\zipalign目錄,它的主要工作是將spk包進行對齊處理,使spk包中的所有資原始檔距離檔案起始偏移為4位元組整數倍,這樣通過記憶體對映訪問apk檔案時速度會更快,驗證apk檔案是否對齊過的工作由ZipAlign.cpp檔案的verify()函式完成,處理對齊的工作則由process()函式完成。

圖例:


Apk的安裝

Apk安裝關鍵物件PackageManagerService:

這個服務負責掃描系統中特定的目錄,找到裡面的應用程式檔案,即以Apk為字尾的檔案,然後對這些檔案進解析,得到應用程式的相關資訊,完成應用程式的安裝過程。

其實就是解析析應用程式配置檔案AndroidManifest.xml的過程,並從裡面得到得到應用程式的相關資訊,例如得到應用程式的元件ActivityServiceBroadcast ReceiverContent Provider等資訊,有了這些資訊後,通過ActivityManagerService這個服務,我們就可以在系統中正常地使用這些應用程式了。

流程概述

1、在SystemServer中利用JNI啟動相關硬體服務

2、 在JNI中執行runtime

3、 ServerThread中啟動PackageManagerServiceActivityManagerService等服務。

4、 PackageManagerService掃描獲得APK檔案並解析安裝

5、 將解析獲得的資料儲存到PackageManagerService

6、從PackageManagerService中獲取圖示並展示

具體流程:

第一步:在SystemServer中利用JNI啟動相關硬體服務

Zygote程序啟動SystemServerJNIinit()初始化方法,初始化SurfaceFlingerSensorServiceAudioFlingerMediaPlayerServiceCameraServiceAudioPolicyService這幾個服務。

第二步:在JNI中執行runtime

JNI的出事華中通過系統全域性唯一的AndroidRuntime例項變數runtimecallStatic來呼叫SystemServerinit2函數了。

第三步:在ServerThread中啟動PackageManagerService和ActivityManagerService等服務

init2中建立一個ServerThread執行緒。在這個執行緒中啟動PackageManagerService服務和ActivityManagerService等其他服務。將這些服務加入到ServiceManager中。而ServiceManagerAndroid系統Binder程序間通訊機制的守護程序。

第四步:PackageManagerService掃描獲得APK檔案並解析安裝

PackageManagerService類的建構函式中開始執行安裝應用程式的過程。

在安裝過程中:

1呼叫scanDirLI函式來掃描移動裝置上的下面這五個目錄中的Apk檔案: /system/framework /system/app /vendor/app/data/app /data/app-private

2將以Apk作為字尾名的檔案呼叫scanPackageLI函式來進行解析和安裝。

第五步:將解析獲得的資料儲存到PackageManagerService中

Apk檔案建立一個PackageParser例項,接著呼叫這個例項的parsePackage函式來對這個Apk檔案進行解析,同時呼叫另外一個版本的scanPackageLI函式把來解析後得到的應用程式資訊packageproviderservicereceiveractivity等資訊儲存在PackageManagerService服務中。每一個Apk檔案都是一個歸檔檔案,它裡面包含了Android應用程式的配置檔案AndroidManifest.xml,這裡主要就是要對這個配置檔案就行解析了,從Apk歸檔檔案中得到這個配置檔案。

第六步:從PackageManagerService中獲取圖示並展示

Android系統通過Home應用程式LauncherPackageManagerService服務中把這些安裝好的應用程式取出來,並以友好的方式在桌面上展現出來,例如以快捷圖示的形式。

從開機到應用的啟動過程

開機流程概述


Zygote是一個虛擬器程序,正如我們在前一個步驟所說的在系統引導的時候啟動。Zygote預載入以及初始化核心庫類。通常,這些核心類一般是隻讀的,也是Android SDK或者核心框架的一部分。在Java虛擬機器中,每一個例項都有它自己的核心庫類檔案和堆物件的拷貝。為了克服Android系統為每一個應用啟動不同的Dalvik虛擬機器例項,會消耗大量的記憶體以及時間問題,Android系統創造了”Zygote”,讓Dalvik虛擬機器共享程式碼、低記憶體佔用以及最小的啟動時間成為可能。

開機具體流程

第一步:載入載入程式到ram

當電源按下,引導晶片程式碼開始從預定義的地方(固化在ROM)開始執行。載入載入程式到RAM,然後執行。

第二步:執行載入程式

載入程式分兩個階段執行。第一個階段,檢測外部的RAM以及載入對第二階段有用的程式;第二階段,載入程式設定網路、記憶體等等。這些對於執行核心是必要的,為了達到特殊的目標,載入程式可以根據配置引數或者輸入資料設定核心。

第三步:完成核心的系統設定

Android核心與桌面linux核心啟動的方式差不多。核心啟動時,設定快取、被保護儲存器、計劃列表,載入驅動。當核心完成系統設定,它首先在系統檔案中尋找”init”檔案,然後啟動root程序或者系統的第一個程序。

第五步:執行init程序(即root程序)

init程序有兩個責任,一是掛載目錄,比如/sys、/dev、/proc,二是執行init.rc指令碼。

對於init.rc檔案,Android中有特定的格式以及規則。在Android中,我們叫做Android初始化語言。
Android初始化語言由四大型別的宣告組成,即Actions(動作)、Commands(命令)、Services(服務)、以及Options(選項)。

Action(動作):動作是以命令流程命名的,有一個觸發器決定動作是否發生。

Service(服務):服務是init程序啟動的程式、當服務退出時init程序會視情況重啟服務。

Options(選項):選項是對服務的描述。它們影響init程序如何以及何時啟動服務。
咱們來看看預設的init.rc檔案。這裡我只列出了主要的事件以及服務。

在這個階段你可以在裝置的螢幕上看到“Android”logo了。

第六步:載入Zygote程序

2)       registerZygoteSocket()為zygote命令連線註冊一個伺服器套接字。

3)       preloadClassed “preloaded-classes”是一個簡單的包含一系列需要預載入類的文字檔案,你可以在<Android Source>/frameworks/base找到“preloaded-classes”檔案。

4)       preloadResources() preloadResources也意味著本地主題、佈局以及android.R檔案中包含的所有東西都會用這個方法載入。

第七步:啟動系統服務

執行環境請求Zygote執行系統服務(系統服務同時使用native以及java編寫,系統服務可以認為是一個程序)。

核心服務:啟動電源管理器;建立Activity管理器;啟動電話註冊;啟動包管理器設定Activity管理服務為系統程序啟動上下文管理器;啟動系統Context Providers;啟動電池服務;啟動定時管理器;啟動感測服務;啟動視窗管理器;啟動藍芽服務;啟動掛載服務。

其他服務:啟動狀態列服務;啟動硬體服務;啟動網路狀態服務;啟動網路連線服務;啟動通知管理器;啟動裝置儲存監視服務;啟動定位管理器;啟動搜尋服務;啟動剪下板服務;啟動登記服務;啟動桌布服務;啟動音訊服務;啟動耳機監聽;啟動AdbSettingsObserver(處理adb命令)。

第八步:傳送開機啟動廣播

一旦系統服務在記憶體中跑起來了,Android就完成了引導過程。在這個時候“ACTION_BOOT_COMPLETED”開機啟動廣播就會發出去。

應用啟動流程概述

概述1

1、安卓系統的Launcher通過Binder程序間通訊機制通知ActivityManagerService,它要啟動一個Activity

2、Launcher通過Binder程序間通訊機制通知ActivityManagerService,它要啟動一個Activity

3、Launcher通過Binder程序間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就建立一個新的程序,用來啟動一個ActivityThread例項,即將要啟動的Activity就是在這個ActivityThread例項中執行;

4、ActivityThread通過Binder程序間通訊機制將一個ApplicationThread型別的Binder物件傳遞給ActivityManagerService,以便以後ActivityManagerService能夠通過這個Binder物件和它進行通訊;

5、ActivityManagerService通過Binder程序間通訊機制通知ActivityThread,現在一切準備就緒,它可以真正執行Activity的啟動操作了。

概述2

1、 Launcher 接收到點選事件,獲取應用的資訊,向 SystemServer(ActivityManagerService 簡稱AMS 執行在裡面) 發起啟動應用的請求。

2、 SystemServer(AMS) 請求 Launcher Pause Launcher 需要儲存狀態進入後臺)

3、 Launcher Pause SystemServer(AMS) 傳送 Pause 完畢

4、 SystemServer(AMS) Zygote 請求啟動一個新程序(calculator)

5、 Zygote fork出新程序(calculator) , 在新程序中執行 ActivityThread 類的 main 方法

6、 calculator SystemServer(AMS) 請求 attach AMS

7、 SystemServer(AMS) 請求 calculator launch

8、 calculator 呼叫 onCreate onResume 回撥

9、 calculator 介面顯示自螢幕上(還需細分)

來自兩篇不同的部落格,所以對比著看。

 

概述2的具體流程

第一步:Launcher 接收到點選事件向AMS發起啟動應用的請求。

1、 Launcher 程序啟動的呼叫棧:ZygoteInit.main ->ActivityThread.main->Looper.loop(進入訊息迴圈)

2、 訊息迴圈中收到系統分發過來的訊息,回撥 onClick 去啟動 Activity

3、 通過ActivityManagerProxy RPC 將請求傳送給 AMS。

第二步:SystemServer(AMS) 請求 Launcher Pause

下面的ST是在 ApplicationThreadProxy 的 schedulePauseActivity 函式列印的呼叫棧。

07-29 07:06:11.922  4151  4183 D zhibinw : java.lang.RuntimeException: ApplicationThreadProxy-schedulePauseActivity
07-29 07:06:11.922  4151  4183 D zhibinw :  at android.app.ApplicationThreadProxy.schedulePauseActivity(ApplicationThreadNative.java:699)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStack.startPausingLocked(ActivityStack.java:842)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStackSupervisor.pauseBackStacks(ActivityStackSupervisor.java:680)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:1638)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1477)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStackSupervisor.resumeTopActivitiesLocked(ActivityStackSupervisor.java:2520)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStack.startActivityLocked(ActivityStack.java:2125)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStackSupervisor.startActivityUncheckedLocked(ActivityStackSupervisor.java:2258)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1560)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:994)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:3415)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3402)
07-29 07:06:11.922  4151  4183 D zhibinw :  at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:140)
07-29 07:06:11.922  4151  4183 D zhibinw :  at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2223)
07-29 07:06:11.922  4151  4183 D zhibinw :  at android.os.Binder.execTransact(Binder.java:446)

這個呼叫棧正好銜接上面的呼叫棧,上面是從 Launcher 通過 ActivityManagerProxy RPC 到 SystemServer(AMS) ,這裡剛好 RPC 切換到 SystemServer(AMS) 程序。通過一系列呼叫 SystemServer(AMS)程序向 Launcher 傳送 pause 的請求,同樣是通過 RPC. 這一來一回,涉及到app 程序和 SystemServer 的互動,涉及到 ActivityManagerNative, ActivityManagerService,ActivityManagerProxy, ApplicationThreadNative,ApplicationThread,ApplicationThreadProxy.一張圖來展示它們的關係, 如下:


第三步:Launcher Pause ,向 SystemServer(AMS) 傳送 Pause 完畢

緊接著,Launcher 會回覆 SystemServer(AMS) , Pause 完畢:

第四步: SystemServer(AMS) 向 Zygote 請求啟動一個新程序(calculator)

然後調到了 SystemServer(AMS) 的 activityPaused,接下來 AMS 嘗試去啟動 calculator 的介面,發現沒有,所以向 Zygote 請求建立一個新的程序。Zygote 存在的目的就是去建立新的程序, SystemServer 通過 socket 和 Zygote 進行通訊。SystemServer 向 Zygote 傳送建立新程序的請求,並獲取pid 和 usingWrapper

第五步: Zygote fork 出新程序(calculator) , 在新程序中執行ActivityThread 類的 main 方法

從Zygote 的 main 函式可以看出, Zygote 的作用:一是fork出 SystemServer , 然後進入迴圈,讀取 socket 來的訊息,響應fork新程序的請求。

Zygote fork 出新程序,分別從子程序和父程序返回。

父程序將結果返回給 SystemServer ,然後繼續自己的 loop, 而子程序進入新的入口 ActivityThread。

ActivityThread 是 Application相關的最重要的一個類了,但是名字起的不好,它叫Thread ,但是不是一個 Thread, 它也不侷限在 Activity 上。

ActivityThread app 程序執行的入口和框架,個人覺得,看懂了這個類,就看懂了 Android app 框架的設計。它負責 app 與 system_server 互動,負責 app 側 activity,service,broadcast,provider 管理。

第六步:calculator 向 SystemServer(AMS) 請求 attach 到 AMS

新程序啟動後的第一件事就是上報(attach)到 SystemServer(AMS) ,使得 SystemServer(AMS) 可以統一管理排程。

第七步:SystemServer(AMS) 請求 calculator launch

SystemServer(AMS) 呼叫scheduleLaunchActivity 通過IPC 傳遞到 ActivityThread 的 scheduleLaunchActivity,在 ActivityThread 裡面它用 Message 的方式傳送自己主執行緒的Handler 來非同步處理。

ActivityThread 作為app入口框架是怎麼做的:1.建立訊息佇列 2.與AMS建立起關聯 3.進入迴圈等待訊息/處理訊息。 也就是說,attach AMS 之後,app 主執行緒裡接下來都是在非同步的方式處理訊息,

第八步:calculator 呼叫 onCreate , onResume 回撥

第九步:calculator 介面顯示自螢幕上