Android 系統啟動時 PackageManagerService 對應用組件信息表的構建過程


PackageManagerService 作為及其重要的系統服務,在系統啟動的時候就會啟動,PMS 在應用開發中為我們提供了很多獲取系統中應用信息的方法,如下所示,那么這些跟應用相關的信息是什么時候放入 PMS 的,PMS 又是如何工作的,下面就來一一探究。

首先說明 PMS 作為一個系統進程中的服務,是不能直接跟應用進程進行交互的,所以在應用中通過 Context 對象的 getPackageManager 方法返回的其實是 PMS 在應用進程的遠程代理,整個過程通過 Binder 機制實現,如果不了解 Binder 的請出門左轉,前幾天有專門文章來解釋 Bindler 和代理模式

一、PackageManager 常用方法

首先來看 PM 的一些常用方法,這些方法大多都是通過 PMS 實現的噢,PMS 的作用主要為管理應用程序包

  1. 獲取方式 Context 對象的 getPackageManager 方法的返回值
  2. 作用:可以與已經安裝的程序相關 package 的信息
  3. getApplicationInfo 以 ApplicationInfo 方式返回指定包名的 ApplicationInfo
  4. getApplicationIcon
  5. getInstalledApplications
  6. getInstalledPackages
  7. queryIntentActivities 返回指定 Intent 的 ResolveInfo 對象,Activity集合
  8. queryIntentService 返回指定 Intent 的 ResolveInfo 對象,Service集合
  9. resolveActivity 返回指定的 Activity
  10. resolveService 返回指定的 Service
  11. 可以根據 ApplicationInfo 中的 flag 值判斷應用的類型,具體如下
  12. FLAG_SYSTEM 系統應用
  13. FLAG_UPDATED_SYSTEM_APP 系統應用升級

這么多方法,獲取到的還是這么重要的信息,那么 PMS 中是如何得到這信息這就是今天的重點,也是 PMS 的工作重點,那就是 APP 信息表的構建過程。下面具體來看

二、App 信息表的構建

系統啟動之后就會注冊各種系統服務,如 WindowManagerService、ActivityManagerService、PackageManagerService ,今天我們使用到的是 PackageManagerService ,簡稱 PMS 。

PMS 的構造函數中就開始解析工作了

  1. 加載 FramWork 資源
  2. 加載核心庫
  3. 掃描指定目錄下的 apk 文件進行解析並將解析到的信息進行存儲

PMS 啟動之后,會掃描系統中已安裝的 APK 目錄,包括系統 APP 的安裝目錄 /sysytem/app, 第三方應用的目錄 /data/app ,PMS 會解析 apk 包下的 AndroidManifest.xml 文件得到 App 的相關信息,而每個 AndroidManifest.xml 又包含了 Activity,Service 等組件的注冊信息,當 PMS 掃描並解析完這些信息之后就建立好了整個 apk 的信息樹。今天的主題就是這些信息的解析和存儲過程

PMS 解析 apk 信息的過程

scanDirLI 函數使用 for 循環遍歷指定目錄下的所有文件,解析其中 apk 類型的文件,忽略其他文件。解析方法為 scanPackageLI

scanPackageLI 首先構造一個 PackageParser 也就是一個 apk 包解析器,調用其 parsePackage 方法,其中會判斷路徑文件的類型,如果是文件夾類型會解析文件夾中的 apk 文件,如果是 apk 文件,直接解析調用 parseMonolithicPackage 方法解析得到 Package 對象

最后將 Package 中解析到的 activities 等列表中的數據添加到 PMS 中的對應列表中

PackageParser 的 parseMonolithicPackage 方法,解析 AndroidManifest.xml 文件得到 Package 對象

  1. 構建 AssetManager
  2. 根據 AssetManager 構建 Resources 資源
  3. 調用 AssetManager 對象的 openXmlResourceParser 獲取 AndroidManifest.xml 解析器 XmlResourceParser 對象
  4. 由 parseBaseApk 方法根據解析器、Resources 資源等得到 Package 對象
  5. 將 Package 返回

parseBaseApk 方法,主要任務是真正的解析 AndroidManifest ,采用 Pull 解析的方式,主要有兩個節點, Application 和 user-permission ,Application 節點時會調用 parseBaseApplication 方法

parseBaseApplication方法,解析 Application 標簽中的信息,返回 Package 對象

  1. 解析獲取應用名、應用圖片以及 Application 標簽中設置的其他屬性
  2. 迭代 Application 元素的所有子元素,Activity,Service、Provider、Receiver 等標簽,通過 parseActivity 等方法返回相應的實例
  3. 將解析到 Activity,Service、Provider、Receiver 實例對象添加到 Parkage 對象的 activities 等列表中
  4. 返回 Parkage 對象

scanPackageLI 方法的最后調用 scanPackageDirtyLI 方法
將 Package 中解析到的 activities 等列表中的數據添加到 PMS 中的對應列表中,PMS 中的這些列表都是靜態的,在類初始化時就會實例化。

上面的解析過程就講應用相關的信息做了解析和保存,以便在我們使用時直接獲取。還有我們最常用的啟動 Activity、Service 等系統組件時 PMS 負責根據 Intent 來匹配具體啟動那個組件,下面就已啟動 Activity 為例,看一下 PMS 對 Intent 的匹配過程。

匹配過程

  1. startActivity 方法最終會調用 startActivityForResult

  2. startActivityForResult 中直接調用 Instrumentation 的 execStartActivity 方法

  3. 通過 ActivityManagerNative.getDefault 方法得到 AMS ,調用 AMS 的 startActivity 方法

  4. AMS 的 startActivity 方法中有一步會調用 PMS 的 resolveIntent 方法,該方法調用了 PMS 自身的 queryIntentActivites 方法,返回一個 ActivityInfo 對象列表,記錄了符合 Intent 的 ActivityInfo,每個 ActivityInfo 對應一個 Activity

  5. queryIntentActivites 方法中先獲取 Intent 的 ComponentName ,不為空則根據 ComponentName 直接獲取 ActivityInfo,精確跳轉情況,也就是顯示的啟動

  6. ComponentName 為空,說明是隱式 Intent ,查看 Intent 是否指定了要跳轉到的目標組件的包名,如果指定了包名則通過包名進行匹配。如果沒有指定包名則通過 Action 、Category 等信息模糊匹配 PMS 中存儲的 Activity 列表中的內容。將匹配到的 ActivityInfo 列表返回

  7. 如果精確跳轉情況,則直接啟動,如果是模糊匹配則彈出選擇窗口。

  8. 跳轉的過程是由 AMS 完成的

總結

  1. 系統啟動時 PMS 啟動,PMS 解析已安裝的應用信息

  2. 通過 Intent 啟動時,根據 Intent 中的信息在 PMS 中查找對應組件,啟動對應組件

APP 信息表的構建以及在啟動系統組件時 Intent 的匹配就是我們在應用開發中使用 PMS 較多的地方,了解 PMS 的工作過程也更有利於我們了解 Android 系統架構


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com