APK安裝流程詳解16——Android包管理總結(jié)

APK安裝流程系列文章整體內(nèi)容如下:
  • 1、設(shè)計(jì)思想
  • 2、PackageManagerService的抽象理解
  • 3、PackageManagerService里面的數(shù)據(jù)結(jié)構(gòu)
  • 4、PackageManagerService的三大流程
  • 5、PackageManagerService的體系結(jié)構(gòu)

一、設(shè)計(jì)思想

如果你是Android 系統(tǒng)中的架構(gòu)師,讓你設(shè)計(jì)一個(gè)Android的安裝系統(tǒng)中的PackageManagerService,你會(huì)怎么設(shè)計(jì)? 既然要設(shè)計(jì),咱們要首先弄清幾個(gè)問(wèn)題,我希望大家看下面的問(wèn)題的時(shí)候,多想兩個(gè)問(wèn)題:1、如果讓你設(shè)計(jì),你怎么設(shè)計(jì)。這個(gè)"類(lèi)"存在意義是什么?

  • 1、為什么關(guān)機(jī)的時(shí)候手機(jī)是磚頭,而開(kāi)機(jī)后,所有APP都可以運(yùn)行了,這是為什么?
  • 2、Android系統(tǒng)是通過(guò)什么手段來(lái)加載已經(jīng)安裝到手機(jī)上應(yīng)用的?
  • 3、既然是加載,按照科學(xué)的架構(gòu)設(shè)計(jì),是不是應(yīng)該存在一個(gè)管理者,來(lái)全局管理,那個(gè)這個(gè)類(lèi)是什么?
  • 4、在安裝一個(gè)APK的時(shí)候,APK是"死的",Android系統(tǒng)是怎么把它變成一個(gè)"活的"APP,他是怎么加載到內(nèi)存中去的

那我們就來(lái)依次來(lái)看下這幾個(gè)問(wèn)題

1、為什么關(guān)機(jī)的時(shí)候手機(jī)是磚頭,而開(kāi)機(jī)后,所有APP都可以運(yùn)行了,它是怎么加載的?

  • 首先明確一點(diǎn),手機(jī)關(guān)機(jī)以后,就是一個(gè)冰冷的磚頭,只能用來(lái)"砸核桃",那開(kāi)機(jī)后,你點(diǎn)擊桌面上的任何一個(gè)圖片,都能開(kāi)啟一個(gè)APP,這說(shuō)明在開(kāi)機(jī)過(guò)程中,系統(tǒng)把已經(jīng)安裝好的APP加載到內(nèi)存中,這到底是怎么做的?所以我們反推斷,在安卓系統(tǒng)中肯定存在這么一塊區(qū)域,用于存放已經(jīng)安裝的APP的信息,在開(kāi)機(jī)的時(shí)候,通過(guò)系統(tǒng)掃描,這塊區(qū)域,把對(duì)應(yīng)的內(nèi)容加載到內(nèi)存中去。
  • 其次,通過(guò)上面的分析,我們知道了在Android系統(tǒng)中存在這樣一塊區(qū)域,在開(kāi)機(jī)的的時(shí)候,加載這塊區(qū)域的信息,從而實(shí)現(xiàn)加載在內(nèi)存中去。那么我們繼續(xù)反推斷,那這塊區(qū)域的信息,是怎么來(lái)的?應(yīng)該在安裝這個(gè)APK的時(shí)候,把這個(gè)APK的信息寫(xiě)入到該區(qū)域的。這樣就可以實(shí)現(xiàn)了在安卓系統(tǒng)一次安裝后,在刪除APK文件后,還可以運(yùn)行APP了

其實(shí)上面的解答是基本上所有操作的系統(tǒng)的安裝思路,大家可以想一下在Windows下是不是也是如此。

上面說(shuō)的Android區(qū)域其實(shí)就是:“/data目錄”下的system目錄,這個(gè)目錄用來(lái)保存很多系統(tǒng)文件。主要工作是創(chuàng)建了5個(gè)位于目錄/data/system的File對(duì)象,分別是:

  • packages.xml:記錄了系統(tǒng)中所有安裝的應(yīng)用信息,包括基本信息、簽名和權(quán)限
  • pakcages-back.xml:packages.xml文件
  • pakcages-stoped.xml:記錄系統(tǒng)中被強(qiáng)制停止的運(yùn)行的應(yīng)用信息。系統(tǒng)在強(qiáng)制停止某個(gè)應(yīng)用的時(shí)候,會(huì)將應(yīng)用的信息記錄在該文件中。
  • pakcages-stoped-backup.xml:pakcages-stoped.xml文件的備份
  • 保存普通應(yīng)用的數(shù)據(jù)目錄和uid等信息

這個(gè)5個(gè)文件中pakcages-back.xml和pakcages-stoped-backup.xml是備份文件。當(dāng)Android對(duì)文件packages.xml和pakcages-stoped.xml寫(xiě)之前,會(huì)先把它們備份,如果寫(xiě)文件成功了,再把備份文件刪除。如果寫(xiě)的時(shí)候,系統(tǒng)出問(wèn)題了,重啟后在需要讀取這兩個(gè)文件時(shí),如果發(fā)現(xiàn)備份文件存在,會(huì)使用備份文件的內(nèi)容,因?yàn)樵次募赡芤呀?jīng)損壞了。其中packages.xmlPackageManagerServcie啟動(dòng)時(shí),需要用到的文件。
我把我的Nexus 6P手機(jī)Root后,在/data/system 截屏如下:

/data/system目錄.png

我把packages.xml導(dǎo)出來(lái),文件內(nèi)容太大,我就直接截屏了,內(nèi)容如下:


截屏1.png
截屏2.png

圖片看不清,可以看下面的縮減版

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="23" databaseVersion="3" fingerprint="google/angler/angler:6.0.1/MTC20L/3230295:user/release-keys" />
    <version volumeUuid="primary_physical" sdkVersion="23" databaseVersion="23" fingerprint="google/angler/angler:6.0.1/MTC19T/2741993:user/release-keys" />
    <permission-trees>
        <item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
    </permission-trees>
    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        <item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
     .....
        <item name="com.android.voicemail.permission.ADD_VOICEMAIL" package="android" protection="1" />
    </permissions>
    <package name="com.google.android.youtube" codePath="/system/app/YouTube" nativeLibraryPath="/system/app/YouTube/lib" primaryCpuAbi="arm64-v8a" publicFlags="945307205" privateFlags="0" ft="11e9134c000" it="11e9134c000" ut="11e9134c000" version="107560144" userId="10075">
        <sigs count="1">
            <cert index="0" key="30820252308201bb02044934987e300d06092a864886f70d01010405003070310b3009060355040613025553310b3009060355040813024341311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c652c20496e6331143012060355040b130b476f6f676c652c20496e633110300e06035504031307556e6b6e6f776e301e170d3038313230323032303735385a170d3336303431393032303735385a3070310b3009060355040613025553310b3009060355040813024341311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c652c20496e6331143012060355040b130b476f6f676c652c20496e633110300e06035504031307556e6b6e6f776e30819f300d06092a864886f70d010101050003818d00308189028181009f48031990f9b14726384e0453d18f8c0bbf8dc77b2504a4b1207c4c6c44babc00adc6610fa6b6ab2da80e33f2eef16b26a3f6b85b9afaca909ffbbeb3f4c94f7e8122a798e0eba75ced3dd229fa7365f41516415aa9c1617dd583ce19bae8a0bbd885fc17a9b4bd2640805121aadb9377deb40013381418882ec52282fc580d0203010001300d06092a864886f70d0101040500038181004086669ed631da4384ddd061d226e073b98cc4b99df8b5e4be9e3cbe97501e83df1c6fa959c0ce605c4fd2ac6d1c84cede20476cbab19be8f2203aff7717ad652d8fcc890708d1216da84457592649e0e9d3c4bb4cf58da19db1d4fc41bcb9584f64e65f410d0529fd5b68838c141d0a9bd1db1191cb2a0df790ea0cb12db3a4" />
        </sigs>
        <perms>
            <item name="com.google.android.c2dm.permission.RECEIVE" granted="true" flags="0" />
            <item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
            <item name="com.google.android.providers.gsf.permission.READ_GSERVICES" granted="true" flags="0" />
            <item name="com.google.android.youtube.permission.C2D_MESSAGE" granted="true" flags="0" />
            <item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.NFC" granted="true" flags="0" />
            <item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
            <item name="com.google.android.gms.permission.AD_ID_NOTIFICATION" granted="true" flags="0" />
            <item name="android.permission.INTERNET" granted="true" flags="0" />
            <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.VIBRATE" granted="true" flags="0" />
            <item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
            <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
        </perms>
        <proper-signing-keyset identifier="11" />
        <domain-verification packageName="com.google.android.youtube" status="0">
            <domain name="youtu.be" />
            <domain name="m.youtube.com" />
            <domain name="youtube.com" />
            <domain name="www.youtube.com" />
        </domain-verification>
    </package>

上面是我手機(jī)packages.xml的一個(gè)片段。我們看下里面的"youtube"應(yīng)用。通過(guò)標(biāo)簽<package>記錄了一個(gè)應(yīng)用的基本信息,簽名和聲明的權(quán)限。

(1)<package>表示包信息,下面我們就來(lái)解釋下標(biāo)簽<package>中的屬性
  • name表示應(yīng)用的包名
  • codePath表示的是apk文件的路徑
  • nativeLibraryPath表示應(yīng)用的native庫(kù)的存儲(chǔ)路徑
  • flags是指應(yīng)用的屬性,如FLAG_SYSTEM、FLAG_PERSISTENT等
  • it表示應(yīng)用安裝的時(shí)間
  • ut表示應(yīng)用最后一次修改的時(shí)間
  • version表示應(yīng)用的版本號(hào)
  • userId表示所屬于的id
(2)<sign>表示應(yīng)用的簽名,下面我們就來(lái)解釋下 標(biāo)簽<sign>中的屬性
  • count表示標(biāo)簽中包含有多少個(gè)證書(shū)
  • cert表示具體的證書(shū)的值
(3)<perms>表示應(yīng)用聲明使用的權(quán)限,每一個(gè)子標(biāo)簽代表一項(xiàng)權(quán)限

通過(guò)上面的內(nèi)容,我們知道Android系統(tǒng)通過(guò)packages.xml文件來(lái)存儲(chǔ)應(yīng)用信息的,所以我們舉一反三,新安裝的APK,肯定是把新安裝的APK相關(guān)信息寫(xiě)入這個(gè)packages.xml文件中,那么怎么把這個(gè)xml文件,映射到內(nèi)存中的? 那我們就來(lái)看第二個(gè)問(wèn)題

2、Android系統(tǒng)是通過(guò)什么手段來(lái)加載已經(jīng)安裝到手機(jī)上應(yīng)用的

上面提到了,應(yīng)用的信息都存儲(chǔ)在packages.xml中的<package>標(biāo)簽里面,那我們是怎么加載到內(nèi)存中去的?大家平時(shí)是存儲(chǔ)數(shù)據(jù)庫(kù)的時(shí)候都是怎么做的?對(duì)的,一般都是一個(gè)實(shí)體類(lèi)對(duì)應(yīng)數(shù)據(jù)庫(kù)中的一個(gè)表;其中每一個(gè)對(duì)象對(duì)應(yīng)的是數(shù)據(jù)庫(kù)中的一條數(shù)據(jù)。同理,Android系統(tǒng)也是這樣設(shè)計(jì)的,<package>標(biāo)簽里面記錄的包信息其實(shí)是一一對(duì)應(yīng)的PackageSetting類(lèi)。

PackageSetting類(lèi)的繼承關(guān)系.png

PackageSetting繼承了PackageSettingBase類(lèi),PackageSettingBase類(lèi)繼承自GrantedPremisson類(lèi)。應(yīng)用的基本信息保存在PackageSettingBase類(lèi)的成員變量中,聲明的權(quán)限保存在GrantedPremissions類(lèi),簽名則保存在SharedUserSetting類(lèi)的成員變量signatures中。標(biāo)簽<package>所標(biāo)識(shí)的應(yīng)用PackageSetting對(duì)象都保存在Setting的mPackages中,定義如下:

// com.android.server.pm.Settings.java

    final HashMap<String, PackageSetting> mPackages =
            new HashMap<String, PackageSetting>();

在packages.xml中除了標(biāo)簽<package>,還有<updated-package>、<cleaning-package>和<renamed-package> 這三種標(biāo)簽。

  • <updated-package> 標(biāo)簽表示升級(jí)包覆蓋的系統(tǒng)應(yīng)用,對(duì)應(yīng)的是PackageSetting,在Settings里面同樣用mPackages 變量表示
  • <cleaning-package> 標(biāo)簽用來(lái)記錄那些已經(jīng)刪除,但是數(shù)據(jù)目錄還暫時(shí)保留的應(yīng)用的信息。對(duì)應(yīng)的是PackageCleanItem。在Settings里面用mPackagesToBeCleaned變量表示
  • <renamed-package> 標(biāo)簽用來(lái)記錄系統(tǒng)中改名的應(yīng)用。它的記錄信息都插入到mSettings的mRenamedPackages對(duì)象中。

其中mPackagesToBeCleaned和mRenamedPackages在mSettings.java的定義如下:

// com.android.server.pm.Settings.java

    // Packages that have been uninstalled and still need their external
    // storage data deleted.
    final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
    
    // Packages that have been renamed since they were first installed.
    // Keys are the new names of the packages, values are the original
    // names.  The packages appear everwhere else under their original
    // names.
    final HashMap<String, String> mRenamedPackages = new HashMap<String, String>();

上面用大量的文筆說(shuō)Settings,那么它是什么東西?下面就讓我繼續(xù)來(lái)看下一個(gè)問(wèn)題

3、既然是加載,按照科學(xué)的架構(gòu)設(shè)計(jì),是不是應(yīng)該存在一個(gè)管理者,來(lái)全局管理,那個(gè)這個(gè)類(lèi)是什么?

這個(gè)類(lèi)就是Settings

Settings是Android的包的全局管理者,用于協(xié)助PackageManagerService保存所有的安裝包信息,同時(shí)用于存儲(chǔ)系統(tǒng)執(zhí)行過(guò)程中的一些設(shè)置,PackageManagerService和Settings之間的類(lèi)圖關(guān)系如下:

PackageManagerService和Settings的關(guān)系.png

大圖地址1

Settings里面有3個(gè)重要的成員變量:mShareUsers,mPackages,mSharedUsers 。如下:

    final ArrayMap<String, SharedUserSetting> mSharedUsers =
            new ArrayMap<String, SharedUserSetting>();
    final ArrayMap<String, PackageSetting> mPackages =
            new ArrayMap<String, PackageSetting>();
  final ArraySet<String, SharedUserSetting> mSharedUsers =
            new ArraySet<String, SharedUserSetting>();
  • mShareUsers是一個(gè)以String類(lèi)型的name為"key",ShareUserSetting對(duì)象為"value"的ArrayMap。
  • mPackages是一個(gè)以String類(lèi)型的name為"key",PackageSetting對(duì)象為"value"的ArrayMap。
  • mSharedUsers 是一個(gè)以String類(lèi)型的name(比如"android.uid.system")為"key",以SharedUserSetting 對(duì)象為"value"的HashMap

其中ShareUserSetting類(lèi)繼承自GrantedPermissions ,內(nèi)部包含一個(gè)ArraySet類(lèi)型的packages ,這個(gè)packages保存了聲明相同的shareUserId的Package的權(quán)限設(shè)置信息(PackageSetting )通過(guò)上面的問(wèn)題,我們知道PackageSetting繼承自PackageSettingBase,同時(shí)PackageSetting中保存著package的多種信息。

如下圖:


PackageSetting.png

上面提到了一個(gè)概念是SharedUserSetting,那么ShareUserSetting的作用什么是什么?那我們就來(lái)看下:

SharedUserSetting用來(lái)描述具有相同的sharedUserId的應(yīng)用信息,它的成員變量packages保存了所有具有相同sharedUserId的應(yīng)用信息引用。這些應(yīng)用的簽名時(shí)相同的,所有只需要在成員變量signatures中保存一份。通過(guò)這個(gè)對(duì)象,Android運(yùn)行時(shí)很容易檢索到某個(gè)應(yīng)用擁有相同的sharedUserId的其他應(yīng)用。其中應(yīng)用的簽名保存在ShardUserSetting類(lèi)的成員變量signatures中。

我們?cè)诳聪到y(tǒng)應(yīng)用的AndroidManifest.xml中會(huì)發(fā)現(xiàn)

<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" 
    package="com.android.settings" coreApp="true"
    android:sharedUserId="android.uid.system" 
    android:versionCode="1" 
    android:versionName="1.0" >
 </manifest>

shareUserId與UID相關(guān),作用是:

  • 1、兩個(gè)或多個(gè)APK或者進(jìn)程聲明了同一種sharedUserId的APK可以共享批次的數(shù)據(jù),并且可以在運(yùn)行在同一進(jìn)程中(相當(dāng)于進(jìn)程是系統(tǒng)的用戶,某些進(jìn)程可以歸為同一用戶使用,相當(dāng)于Linux系統(tǒng)中的GroupId)
  • 2、通過(guò)聲明特定的sharedUserId,該APK所在的進(jìn)程將被賦予指定的
      2.通過(guò)聲明特定的sharedUserId,該APK所在的進(jìn)程將被賦予指定的UID,將被賦予該UID特定的權(quán)限。

在Settings中和用戶有關(guān)的還有兩個(gè)重要變量,即mUserIds 和mOtherUserIds

    private final ArrayList<Object> mUserIds = new ArrayList<Object>();
    private final SparseArray<Object> mOtherUserIds =
            new SparseArray<Object>();

他們都是以UID為索引,得到對(duì)應(yīng)的ShardUserSetting對(duì)象。更多的關(guān)于Android系統(tǒng)中關(guān)于"用戶"的信息,在后面"用戶模塊"再單獨(dú)講解

4、在安裝一個(gè)APK的時(shí)候,APK是"死的",Android系統(tǒng)是怎么把它變成一個(gè)"活的"APP,他是怎么加載到內(nèi)存中去的

這里就不得不提一下PackageParser這個(gè)類(lèi),這個(gè)類(lèi)負(fù)責(zé)在APK文件安裝的時(shí)候,解析AndroidManifest。

在Android中,Settings提供可持續(xù)化的包信息管理,PackageSetting是一個(gè)存儲(chǔ)單元,表示一個(gè)pkg信息。我們?cè)诮馕鯝PK安裝包的時(shí)候,會(huì)用到PackageParser,在PackageParser里面有一個(gè)字段是PackageParse.Package。這個(gè)PackageParse.Package其實(shí)是對(duì)應(yīng)的上面packages.xml里面的<package>標(biāo)簽。同時(shí)PackageParse.Package也可以理解為pkg信息在內(nèi)存中的一個(gè)實(shí)時(shí)信息,關(guān)機(jī)后變消失,重啟后重新生成,所以PackageParse.Package中的信息一致保證最新。PackageParse.Packag、Settings和PackageSetting三者的關(guān)系如下:

關(guān)系.png

Settings中保存了一個(gè)包名和PackagesSetting的映射表,PackageParse.Package中的mExtras引用指向了對(duì)應(yīng)的PackageSetting實(shí)例,而PackageParse中保存了一個(gè)PackageParse.Package列表

PackageParse.png

從上到下,介紹如下:
1:PackageParser.Package對(duì)應(yīng)一個(gè)apk完整的原始數(shù)據(jù)
2:PackageSetting包含一個(gè)PackageParser對(duì)象實(shí)例,說(shuō)明它也對(duì)應(yīng)一個(gè)apk包的數(shù)據(jù),不同的是,它還包括apk相關(guān)配置數(shù)據(jù),比如apk內(nèi)部哪些component是被disable等。
3:Settings包含了PackageSetting對(duì)象列表,也就是說(shuō)它包含了系統(tǒng)所有apk數(shù)據(jù),還有就是PackageParser,顧名思義,負(fù)責(zé)APK數(shù)據(jù)解析
4:PackageManagerService是全局的包管理器

5、補(bǔ)充一點(diǎn):

Settings里面的主要關(guān)聯(lián)關(guān)系如下圖:


主要關(guān)聯(lián)關(guān)系.png

二、PackageManagerService的抽象理解

上面說(shuō)了很多,我們?cè)偕仙粋€(gè)高度,PackageManagerService到底應(yīng)該怎么去理解它?

每一個(gè)組織結(jié)構(gòu),都有一套自己的管理機(jī)制,比如任何一家公司,都會(huì)存在下面三個(gè)元素:管理者(經(jīng)理)、被管理者(員工)、管理機(jī)制(公司的規(guī)章制度及KPI考核等)。同理在Android的系統(tǒng)的世界里面,也有一家公司叫"包管理"。如果要研究Android的包管理機(jī)制,同樣可以從以下幾個(gè)角度來(lái)思考?

  • 管理者是誰(shuí),他的職責(zé)是什么?
  • 被管理者是誰(shuí),他的職責(zé)是什么?
  • 管理機(jī)制是什么,它是如何運(yùn)轉(zhuǎn)的?

所謂包,其實(shí)就是一種文件的格式,比如APK包,JAR包等,在Android中存活著很多包,所有的應(yīng)用程序都是APK包,很多構(gòu)成Android運(yùn)行環(huán)境的都是JAR包,還有一些以so為后綴的庫(kù)文件,包管理者很重要的一個(gè)職責(zé)就是識(shí)別不同的包,統(tǒng)一維護(hù)這些包的信息。當(dāng)有一個(gè)包進(jìn)入(安裝)或者離開(kāi)(卸載)Android世界,都需要向包管理者申報(bào),其他管理部分要獲取包的具體信息,也都需要向包管理者申請(qǐng)。

如同一家公司是由人與人協(xié)作工作的,不同包之間也需要進(jìn)行協(xié)作。既然有協(xié)作,自然就有協(xié)作的規(guī)范,一個(gè)包可以干什么,不可以干什么,都需要有一個(gè)明確的范圍界定,這就是包管理中的權(quán)限設(shè)計(jì)。涉及到的內(nèi)容非常廣泛,Android的權(quán)限管理、SELinux,都是包管理中權(quán)限設(shè)計(jì)的組成部分。同理Android的世界就像一個(gè)井然有序的一家公司,既有包管理部門(mén),也有其他各種管理部門(mén),比如電源管理部門(mén),窗口管理部門(mén)等等。大家不僅各司其職,而且也有來(lái)往。比如在APK安裝到Activity的顯示,看著很簡(jiǎn)單的過(guò)程,其實(shí)卻需要大量的管理部門(mén)參與進(jìn)來(lái),不斷地進(jìn)行數(shù)據(jù)解析、封裝、傳遞、呈現(xiàn),其內(nèi)部機(jī)制十分復(fù)雜。

現(xiàn)在大家想一下上面三個(gè)問(wèn)題的答案,我詳細(xì)大部分人的前兩個(gè)答案是一致的,管理者是PackageManagerService,被管理是各種"包",最后一個(gè)答案是各有千秋,這里是沒(méi)有標(biāo)準(zhǔn)答案的,希望大家能自己找到自己的答案。

三、PackageManagerService里面的數(shù)據(jù)結(jié)構(gòu)

PackageManagerService涉及的數(shù)據(jù)結(jié)構(gòu)非常多,在分析源碼時(shí),很容易陷入各種數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系,難以自拔,以至于看不到包管理的全貌。我在這里簡(jiǎn)單的總結(jié)了一下各個(gè)數(shù)據(jù)結(jié)構(gòu)的職能如下:

  • PackageManangerService :包管理的核心服務(wù)
  • com.android.server.pm.Settings :所有包的管理信息
    • com.android.server.pm.PackageSetting :?jiǎn)我话男畔?/li>
    • com.android.server.pm.BasePermission :系統(tǒng)中已有的權(quán)限
    • com.android.server.pm.PermissionState :授權(quán)狀態(tài)

—————————————分隔符—————————————

  • PackageParser:包解析器
    • PackageParser.Package :解析得到的包信息
    • PackageParser.Component :組件的基類(lèi),其子類(lèi)對(duì)應(yīng)到AndroidManifest.xml中定義的不同組件
      • PackageParser.Activity 對(duì)應(yīng)AndroidManifest.xml中定義<Activity>和<Receiver>標(biāo)簽
      • PackageParser.Service :對(duì)應(yīng)AndroidManifest.xml中定義<Service/>標(biāo)簽
      • PackageParser.Provider :對(duì)應(yīng)AndroidManifest.xml中定義<Provider/> 標(biāo)簽
      • PackageParser.Instrumentation :對(duì)應(yīng)AndroidManifest.xml中定義<Instrumentation/> 標(biāo)簽
      • PackageParser.Permission :對(duì)應(yīng)AndroidManifest.xml中定義<permission/> 標(biāo)簽
      • PackageParser.PermissionGroup :對(duì)應(yīng)AndroidManifest.xml中定義<permission-group/> 標(biāo)簽
    • PackageLite :輕量的包信息
    • ApkLite :輕量級(jí)的APK信息
    • IntentInfo :組件所定義的<intent-filter/>信息,保存了每個(gè)<intent-filter/>節(jié)點(diǎn)的信息,是基類(lèi),它的子類(lèi)是ActivityIntentInfo、ServiceIntentInfo和ProviderIntentInfo
      • ActivityIntentInfo :保存<activity/>和<Receiver/>節(jié)點(diǎn)下的<intent-filter/>節(jié)點(diǎn)
      • ServiceIntentInfo :保存<service/>節(jié)點(diǎn)下的<intent-filter/>節(jié)點(diǎn)
      • ProviderIntentInfo:保存<provider/> 節(jié)點(diǎn)下的<intent-filter/>節(jié)點(diǎn)
PackageParser的數(shù)據(jù)結(jié)構(gòu).png

—————————————分隔符—————————————

  • PackageInfo :跨進(jìn)程傳遞的包數(shù)據(jù),包解析時(shí)生成
    • PackageItemInfo :一個(gè)應(yīng)用包內(nèi)所有組件項(xiàng)和通用信息的基類(lèi)。提供最基本的屬性集,如:label、icon、meta-data等。
      • ApplicationInfo:代表一個(gè)特定應(yīng)用的基本信息,對(duì)應(yīng)AndroidManifest里面的<application>
      • InstrumentationInfo:用作進(jìn)行instrumentation的測(cè)試的片段,對(duì)應(yīng)AndroidManifest里面的<instrumentation>
      • PermissionInfo:代表一個(gè)特定的權(quán)限,對(duì)應(yīng)AndroidManifest里面的<permission/>
      • PermissionGroupInfo :一個(gè)特定的權(quán)限組,對(duì)應(yīng)AndroidManifest里面的<permission-group/>
      • ComponentInfo:代表一個(gè)應(yīng)用內(nèi)組件(如activityInfo、serviceInfo、ProviderInfo)通用信息的基類(lèi)。一般不會(huì)直接使用該類(lèi),它設(shè)計(jì)為了不同應(yīng)用的組件共享統(tǒng)一的定義。
        • ActivityInfo :對(duì)應(yīng)AndroidManifest.xml里面的注冊(cè)的<activity/>標(biāo)簽和<receiver/>標(biāo)簽。代表一個(gè)Activity或者receiver
        • ServiceInfo :對(duì)應(yīng)AndroidManifest.xml里面的注冊(cè)的<service/>標(biāo)簽。代表一個(gè)service
        • ProviderInfo :對(duì)應(yīng)AndroidManifest.xml里面的注冊(cè)的<service/>標(biāo)簽。代表一個(gè)Provider
  • Intent:根據(jù)特定的條件找到匹配的組件
  • IntentFilter :Intent過(guò)濾器
    • ResolveInfo
    • IntentResolver :Intent解析器,其子類(lèi)用于不同組件的Intent解析
      保存了所有<activity/>或者<receiver/>節(jié)點(diǎn)信息。(Activity或者BroadcastReceiver信息就是用該自定義類(lèi)保存的)
      • ActivityIntentResolver :保存所有<activity/>和<receiver/>節(jié)點(diǎn)信息。(Activity或者BroadcastReceiver信息就是用該自定義類(lèi)保存的)
        保存了所有<service/>節(jié)點(diǎn)信息。(Service信息就是用該自定義類(lèi)保存的)。
      • ServiceIntentResolver :保存了所有<service /> 節(jié)點(diǎn)信息。(Service信息就是用該自定義類(lèi)保存的)
      • ProviderIntentReslover:保存了所有 <provider /> 節(jié)點(diǎn)信息
  • PackageHandler :包管理的消息處理器
    • HandlerParams :消息的數(shù)據(jù)載體
      • InstallParams : 用于APK的安裝
      • MeasureParams:用于查詢(xún)某個(gè)已安裝的APK占據(jù)存儲(chǔ)空間的大小(例如在設(shè)置程序中得到某個(gè)APK的緩存文件大小)
      • MoveParams :用于已安裝APK的位置移動(dòng)
    • InstallArgs :APK的安裝參數(shù)
      • FileInstallArgs :針對(duì)是安裝在內(nèi)部存儲(chǔ)的APK
      • AsecInstallArgs :針對(duì)安裝在SD卡上的APK
      • MoveInfoArgs : 移動(dòng)APK
        這么龐大的數(shù)據(jù)結(jié)構(gòu),其各個(gè)數(shù)據(jù)結(jié)構(gòu)的類(lèi)圖如下:

數(shù)據(jù)結(jié)構(gòu).png

大圖地址3

四、PackageManagerService的三大過(guò)程組

如果大家想對(duì)Android系統(tǒng)有一個(gè)大致的了解,就必須要要了解PackageManagerService的三大流程

  • 1、包掃描的過(guò)程組:
    即Android將一個(gè)APK文件的靜態(tài)信息轉(zhuǎn)化為可以管理的數(shù)據(jù)結(jié)構(gòu)
  • 2、包安裝的過(guò)程組:
    即包管理接納一個(gè)新成員的體現(xiàn)。
  • 3、包查詢(xún)的過(guò)程組:
    即Intent的定義和解析是包查詢(xún)的核心,通過(guò)包查詢(xún)服務(wù)可以獲取到一個(gè)包的信息

下面我們來(lái)一一進(jìn)行簡(jiǎn)單的介紹

(一)、包掃描過(guò)程組——即開(kāi)機(jī)掃描過(guò)程

1、為什么要進(jìn)行包掃描?

掃描目錄的目的:

掃描Android系統(tǒng)的幾個(gè)目標(biāo)文件中的APK,從而建立合適的數(shù)據(jù)結(jié)構(gòu)以及管理諸如Package信息、四大組件、授權(quán)信息等各種信息。抽象的地看,PackageManagerService像一個(gè)工廠,它解析實(shí)際的物理文件(APK文件),以及生成符合自己要求的產(chǎn)品。比如PackageManagerService將解析APK包中的AndroidManifest.xml,并根據(jù)其中聲明的Activity標(biāo)簽來(lái)創(chuàng)建與此對(duì)應(yīng)的對(duì)象,并保存到PackageParser.Package類(lèi)型的變量中,然后通過(guò)PackageManagerService的scanPackageDirtyLI()方法將解析后的組件數(shù)據(jù)統(tǒng)計(jì)到PackageManagerService的本地變量中,用于管理查詢(xún)調(diào)用,當(dāng)系統(tǒng)中任意某個(gè)APK的package發(fā)生改變時(shí),如卸載,升級(jí)等操作都會(huì)更新package的統(tǒng)計(jì)數(shù)據(jù)到PackageManagerService,PackageManagerService正式基于擁有系統(tǒng)中所有的Package的信息才能勝任"包管理"這個(gè)管理者的角色。PackageManagerService的工作流程相對(duì)簡(jiǎn)單,復(fù)雜的是其中用于保存各種信息的數(shù)據(jù)結(jié)構(gòu)和它們的關(guān)聯(lián)關(guān)系,以及對(duì)應(yīng)影響結(jié)果的策略控制(比如系統(tǒng)應(yīng)用和普通應(yīng)用)

2、包掃描過(guò)程組的不同理解

如果把包掃描過(guò)程組看成一件事,那么這件事就是:

調(diào)用PackageManagerService類(lèi)的靜態(tài)方法main()方法來(lái)獲取PackageManagerService對(duì)象

如果把包掃描過(guò)程組看成兩件事,那么這兩件事就是

1、創(chuàng)建PackageManagerService對(duì)象
2、將PackageManagerService向ServiceManager注冊(cè),即加入SMS,方便后續(xù)其他進(jìn)程或者app通過(guò)ServiceManager獲得PackageManagerService服務(wù)。

如果把包掃描過(guò)程組看成三件事,那么這三件事是:

1、先讀取保存在packages.xml中記錄的系統(tǒng)關(guān)機(jī)前記錄所有安裝的APP信息, 將其保存在PackageManagerServiced中mSettings中的mPackages中。
2、掃描指定的若干目錄中的app,并把信息記錄在PackageManagerServiced的mPackages中。
3、最后上面的兩者進(jìn)行對(duì)比,看是否有升級(jí)的APP,然后進(jìn)行相關(guān)處理,最后寫(xiě)入package.xml中

當(dāng)然換一個(gè)角度,以掃描角度來(lái)看,也可以把包掃描分解成另外三個(gè)階段:

  • 掃描目標(biāo)文件夾之前的準(zhǔn)備工作
  • 掃描目標(biāo)文件夾
  • 掃描目標(biāo)文件夾之后的工作

如果把包掃描過(guò)程組看成四件事,那么這四件事是:

1、讀取響應(yīng)的配置文件
2、優(yōu)化APK和Jar包
3、掃描系統(tǒng)中所有安裝的應(yīng)用
4、把掃描出的所有應(yīng)用信息進(jìn)行保存

如果把包掃描過(guò)程組劃分的更細(xì),則我將其分為6大步驟

  • 1、變量初始化,包括mSettings,mInstaller,mPackageDexOptimizer等等
  • 2、讀取配置文件
  • 3、掃描系統(tǒng)Package,包含Dex優(yōu)化
  • 4、保存掃描信息
  • 5、掃描非系統(tǒng)應(yīng)用
  • 6、更新數(shù)據(jù)

如果把包掃描過(guò)程組劃分的更細(xì),則我將其分為9大步
第一步:創(chuàng)建Settings對(duì)象,并調(diào)用其addSharedUserLPw()方法,保存ShareUserSetting信息
第二步:創(chuàng)建Installer對(duì)象,用于Native進(jìn)程installd交互
第三步:創(chuàng)建ThreadHandler線程,并以其Looper為參數(shù)創(chuàng)建PackageHandler對(duì)象,用于程序的安裝和卸載
第四步:根據(jù)Installer對(duì)象和/data/user文件對(duì)象創(chuàng)建UserManager對(duì)象,用于多用戶管理
第五步:調(diào)用readPermissions()方法,從/system/etc/permissions目錄下的XML文件讀取權(quán)限信息
第六步:調(diào)用Settings對(duì)象的readLPw()方法解析/data/system目錄下的文件:
第七步:掃描/system/frameworks目錄以及BOOTCLASSPATHplatform.xml定義的系統(tǒng)目錄下的jar和APK文件是否需要dex優(yōu)化,如果需要?jiǎng)t調(diào)用Installer.dexopt()方法來(lái)發(fā)送消息給installd讓它優(yōu)化;如果任意一個(gè)文件執(zhí)行了dex優(yōu)化操作,刪除/data/dalvik-cache目錄下的緩存文件
第八步:創(chuàng)建AppDirObserver對(duì)象監(jiān)聽(tīng)/system/frameworks、/system/app、/vendor/app(廠商定制)、/data/app、/data/app-private5個(gè)目錄,并調(diào)用scanDirLI()方法掃描其中的APK文件:
第九步:匯總上面掃描XML和APK得到的信息,并寫(xiě)入文件;

3、如果把包掃描過(guò)程組劃分為"方法級(jí)"的流程,如下圖:

開(kāi)機(jī)掃描流程.png

大圖地址3

4、溫馨提醒

  • 在packages.xml中<package>標(biāo)簽記錄的APP的安裝信息。有獨(dú)立uid的APP,后面再反序列化的時(shí)候,會(huì)映射為PackageSetting對(duì)象,保存在mSettings的mPackages中;有sharedUid的APP,后面反序列化的時(shí)候,會(huì)映射為PendingPackage對(duì)象,保存在mSettings的mPendingPackages中。
  • 對(duì)于<share-user>標(biāo)簽記錄的的share uid信息,封裝為SharedUserSetting對(duì)象,保存到mSettings里面的mSharedUser中,在此過(guò)程中遇到的uid和 shared uid都保存在mUserIds中,并讓每個(gè)uid指向與之關(guān)聯(lián)的PackageSetting對(duì)象,或者SharedUserSetting對(duì)象

5、小結(jié)

  • PackageManagerService是伴隨著系統(tǒng)進(jìn)程啟動(dòng)而啟動(dòng)的,最終會(huì)構(gòu)造一個(gè)PackageManagerService對(duì)象,此后,PackageManagerService將成為Android世界的包管理者,對(duì)外提供包的增、刪、改、查的操作
  • 在PackageManagerService的啟動(dòng)過(guò)程中,最重要的是對(duì)所有靜態(tài)APK文件進(jìn)行掃描,生成一個(gè)在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)Package,PackageManagerService實(shí)際上就是維護(hù)這所有在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)。已有的包的歷史信息會(huì)寫(xiě)入磁石,PackageManagerService的Settings專(zhuān)門(mén)來(lái)管理寫(xiě)入磁盤(pán)的包信息。
  • 所有包的信息掃描完成后,需要對(duì)應(yīng)用進(jìn)行授權(quán),這是Android權(quán)限管理的一部分。隨著 Android版本的升級(jí),授權(quán)機(jī)制略有區(qū)別,總體框架是:每個(gè)APK都可以聲明權(quán)限,并為權(quán)限設(shè)定保護(hù)級(jí)別,其他APK需要使用這些權(quán)限的時(shí)候,需要先申請(qǐng),再由系統(tǒng)判定是否進(jìn)行授權(quán)。

(二)、包安裝的過(guò)程組——即安裝一個(gè)新的APK

安裝一個(gè)APK的其大致流程如下:


大致流程.png

通常,安裝一個(gè)APK 通常分為以下4種方式

  • 安裝系統(tǒng)應(yīng)用
  • 網(wǎng)絡(luò)下載應(yīng)用安裝
  • ADB工具安裝
  • 第三方應(yīng)用安裝

下面我們就依次介紹下

1、 安裝系統(tǒng)應(yīng)用

系統(tǒng)的應(yīng)用的安裝主要在PackageManagerService的main方法里面進(jìn)行操作的

其順序如下:
第一步:PackageManagerService.main()初始化注冊(cè)
第二步:建立Java層的installer與C層的intalld的socket聯(lián)接
第三步:建立PackageHandler消息循環(huán)
第四步:調(diào)用成員變量mSettings的readLPw()方法恢復(fù)上一次的安裝信息
第五步:.jar文件的detopt優(yōu)化
第六步:scanDirLI函數(shù)掃描特定目錄的APK文件解析
第七步:updatePermissionsLPw()函數(shù)分配權(quán)限
第八步:調(diào)用mSettings.writeLPr()保存安裝信息

2、 網(wǎng)絡(luò)下載應(yīng)用安裝

其順序如下:
第一步:調(diào)用PackageManagerService的installPackage方法
第二步:上面的方法調(diào)用installPackageWithVerfication(),進(jìn)行權(quán)限校驗(yàn),發(fā)送INIT_COPY的msg
第三步:進(jìn)入PackageManagerService的doHandleMessage方法的INIT_COPY分支
第四步:成功綁定了com.android.defcontainer.DefaultContainerService服務(wù),進(jìn)入MCS_BOUND分支
第五步:里面調(diào)用PackageManagerService中內(nèi)部抽象類(lèi)HandlerParams的子類(lèi)InstallParams的startCopy方法。
第六步:抽象類(lèi)的HandlerParams的startCopy方法調(diào)用了HandlerParams子類(lèi)的handleStartCopy和handlerReturnCode兩個(gè)方法
第七步:handlesStartCopy方法調(diào)用了InstallArgs的子類(lèi)copyApk,它負(fù)責(zé)將下載的APK文件copy到/data/app
第八步:handleReturnCode調(diào)用handleReturnCode方法
第九步:調(diào)用PackageManagerService服務(wù)的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法進(jìn)行APK掃描。
第十步:上面的方法判斷是否APP應(yīng)安裝,調(diào)用installNewPackageLI或replacePackageLI方法
第十一步:調(diào)用updateSettingsLI方法進(jìn)行更新PackageManagerService的Settings
第十二步:發(fā)送what值為POST_INSTALL的Message給PackageHandler進(jìn)行處理
第十三步:發(fā)送what值為MCS_UNBIND的Message給PackageHandler,進(jìn)而調(diào)用PackageHandler.disconnectService()中斷連接

3、 ADB工具安裝

Android Debug Bridge (adb)是SDK自帶的管理設(shè)備的工具,通過(guò)ADB命令的方式也可以為手機(jī)或者模擬器安裝應(yīng)用,其入口函數(shù)為pm.java

Android Debug Bridge (adb) 是SDK自帶的管理設(shè)備的工具,通過(guò)ADB命令行的方式也可以為手機(jī)或模擬器安裝應(yīng)用,其入口函數(shù)源文件為pm.java

其順序如下:
第一步:pm.java的runInstall()方法
第二步:參數(shù)不對(duì)會(huì)調(diào)用showUsage方法,彈出使用說(shuō)明
第三步:正常情況runInstall會(huì)調(diào)用mPm變量的installPackageWithVerification方法
第四步:由于pm.java中的變量mPm是PackageManagerService的實(shí)例,所以實(shí)際上是調(diào)用PackageManagerService的installPackageWithVerfication()方法
第五步:進(jìn)入PackageManagerService的doHandleMessage方法的INIT_COPY分支
第六步:成功綁定了com.android.defcontainer.DefaultContainerService服務(wù),進(jìn)入MCS_BOUND分支
第七步:里面調(diào)用PackageManagerService中內(nèi)部抽象類(lèi)HandlerParams的子類(lèi)InstallParams的startCopy方法。
第八步:抽象類(lèi)的HandlerParams的startCopy方法調(diào)用了HandlerParams子類(lèi)的handleStartCopy和handlerReturnCode兩個(gè)方法
第九步:handlesStartCopy方法調(diào)用了InstallArgs的子類(lèi)copyApk,它負(fù)責(zé)將下載的APK文件copy到/data/app
第十步:handleReturnCode調(diào)用handleReturnCode方法
第十一步:調(diào)用PackageManagerService服務(wù)的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法進(jìn)行APK掃描。
第十二步:上面的方法判斷是否APP應(yīng)安裝,調(diào)用installNewPackageLI或replacePackageLI方法
第十三步:調(diào)用updateSettingsLI方法進(jìn)行更新PackageManagerService的Settings
第十四步:發(fā)送what值為POST_INSTALL的Message給PackageHandler進(jìn)行處理
第十五步:發(fā)送what值為MCS_UNBIND的Message給PackageHandler,進(jìn)而調(diào)用PackageHandler.disconnectService()中斷連接

ADB安裝.png
4、 第三方應(yīng)用安裝

第一步:調(diào)用PackageInstallerActivity的onCreate方法初始化安裝界面
第二步:初始化界面以后調(diào)用initiateInstall方法
第三步:上面的方法調(diào)用startInstallConfirm方法,彈出確認(rèn)和取消安裝的按鈕
第四步:點(diǎn)擊確認(rèn)按鈕,打開(kāi)新的activity:InstallAppProgress
第五步:InstallAppProgress類(lèi)初始化帶有進(jìn)度條的界面之后,調(diào)用PackageManager的installPackage方法
第六步:PackageManager是PackageManagerService實(shí)例,所以就是調(diào)用PackageManagerService的installPackage方法
第七步:調(diào)用PackageManagerService的installPackage方法
第八步:上面的方法調(diào)用installPackageWithVerfication(),進(jìn)行權(quán)限校驗(yàn),發(fā)送INIT_COPY的msg
第九步:進(jìn)入PackageManagerService的doHandleMessage方法的INIT_COPY分支
第十步:成功綁定了com.android.defcontainer.DefaultContainerService服務(wù),進(jìn)入MCS_BOUND分支
第十一步:里面調(diào)用PackageManagerService中內(nèi)部抽象類(lèi)HandlerParams的子類(lèi)InstallParams的startCopy方法。
第十二步:抽象類(lèi)的HandlerParams的startCopy方法調(diào)用了HandlerParams子類(lèi)的handleStartCopy和handlerReturnCode兩個(gè)方法
第十三步:handlesStartCopy方法調(diào)用了InstallArgs的子類(lèi)copyApk,它負(fù)責(zé)將下載的APK文件copy到/data/app
第十四步:handleReturnCode調(diào)用handleReturnCode方法
第十五步:調(diào)用PackageManagerService服務(wù)的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法進(jìn)行APK掃描。
第十六步:上面的方法判斷是否APP應(yīng)安裝,調(diào)用installNewPackageLI或replacePackageLI方法
第十七步:調(diào)用updateSettingsLI方法進(jìn)行更新PackageManagerService的Settings
第十八步:發(fā)送what值為POST_INSTALL的Message給PackageHandler進(jìn)行處理
第十九步:發(fā)送what值為MCS_UNBIND的Message給PackageHandler,進(jìn)而調(diào)用PackageHandler.disconnectService()中斷連接

流程.png

點(diǎn)擊放大查看高清無(wú)碼大圖

小結(jié):

  • 1、安裝和卸載都是通過(guò)PackageManager,實(shí)質(zhì)上是實(shí)現(xiàn)了PackageManagerService來(lái)完成具體的操作,所有細(xì)節(jié)和邏輯均可以在PackageManagerService中跟蹤查看。
  • 2、所有安裝方式殊途同歸,最終就是回到PackageManagerService中,然后調(diào)用底層本地代碼的installd來(lái)完成的。
  • 3、APK的安裝過(guò)程主要分為以下幾步:
    - 拷貝到apk文件到指定目錄
    - 解壓縮apk,拷貝文件,創(chuàng)建應(yīng)用的數(shù)據(jù)目錄
    - 解析apk的AndroidManifest.xml文件
    - 向Launcher應(yīng)用申請(qǐng)?zhí)砑觿?chuàng)建快捷方式

(三)、包查詢(xún)的過(guò)程組——即解析Intent并找到其配備的組件

1、包管理是以什么樣的形式對(duì)外提供服務(wù)那?

在寫(xiě)應(yīng)用程序時(shí),我們通常會(huì)利用應(yīng)用自身的上下文環(huán)境Context來(lái)獲取包管理服務(wù),如下:

// 獲取一個(gè)PackageManager的對(duì)象實(shí)例
PackageManager pm = context.getPackageManager();
// 通過(guò)PackageManager對(duì)象獲取指定包名的包信息
PackageInfo pi = pm.getPackageInfo("com.android.contacts", 0);

這么一段簡(jiǎn)單的代碼,其實(shí)蘊(yùn)含很多的深意

  • 1、上面已經(jīng)講解過(guò)了PackageManagerService和其管理的各種數(shù)據(jù)結(jié)構(gòu),都是運(yùn)行在系統(tǒng)進(jìn)程之中。在應(yīng)用進(jìn)程中獲取的PackageManager對(duì)象,只是PackageManagerService在應(yīng)用進(jìn)程中的一個(gè)代理,不同的應(yīng)用進(jìn)程都有不同的代理,意味著不同應(yīng)用進(jìn)程中的PackageManager是不同的,但是管理者PackageManagerService有且只有一個(gè)
  • 2、運(yùn)行在應(yīng)用進(jìn)程中的PackageManager要與運(yùn)行在系統(tǒng)進(jìn)程中的PackageManagerService進(jìn)行通信,通信手段是Android中最常見(jiàn)的Binder機(jī)制。因此會(huì)有一個(gè)IPackageManager.aidl文件,用于描兩者通信的接口。另外,應(yīng)用進(jìn)程中的PackageInfo對(duì)象。PackageInfo其實(shí)就是由系統(tǒng)進(jìn)程傳遞到應(yīng)用進(jìn)程的對(duì)象
IPackageManager.png

PackageManagerService作為包管理的最核心組成部分,伴隨著系統(tǒng)的啟動(dòng)而創(chuàng)建,并一直運(yùn)行系統(tǒng)進(jìn)程中。當(dāng)應(yīng)用程序需要獲取包管理服務(wù)時(shí),會(huì)生成一個(gè)PackageManager對(duì)PackageManagerService進(jìn)行通信。在包解析時(shí)就會(huì)生成包信息,即XXInfo這一類(lèi)數(shù)據(jù)結(jié)構(gòu),PackageManagerService將這些數(shù)據(jù)傳遞給需要的應(yīng)用進(jìn)程。

管理者對(duì)內(nèi)設(shè)計(jì)了復(fù)雜的管理機(jī)制,對(duì)外封裝了簡(jiǎn)單的使用接口。這種設(shè)計(jì)在Android中大量出現(xiàn),比如ActivityManagerService、WindowManagerService、PowerManagerService等,基本所有的系統(tǒng)服務(wù)都遵循這種設(shè)計(jì)規(guī)范。對(duì)于應(yīng)用程序而言,不需關(guān)心管理者的實(shí)現(xiàn)原理,只需要理解接口的使用場(chǎng)景

Android在全局定義了IPackageManager,接口,描述了包管理者對(duì)外提供的功能,運(yùn)行在系統(tǒng)進(jìn)程中的PackageManagerService實(shí)現(xiàn)了IPackageManager接口,作為包管理的服務(wù)端,客戶端通過(guò)IPackageManager接口請(qǐng)求包服務(wù)。為了方便客戶端進(jìn)行包服務(wù),Android做了多層的封裝。應(yīng)用進(jìn)程作為客戶端,通過(guò)PackageManager便可使用包服務(wù),客戶端實(shí)際存在的對(duì)象是ApplicationPackageManager,它封裝了IPackageManager的所有接口。在應(yīng)用進(jìn)程來(lái)看,客戶端和服務(wù)端的概念是模糊的,明確的只有運(yùn)行環(huán)境的概念,即Context。包服務(wù)就存在于應(yīng)用進(jìn)程的運(yùn)行環(huán)境中,需要時(shí)直接拿出來(lái)使用即可。

“運(yùn)行環(huán)境(Context)”是Android的設(shè)計(jì)哲學(xué)之一,Android有意弱化進(jìn)程,強(qiáng)化運(yùn)行環(huán)境,這是面向應(yīng)用開(kāi)發(fā)者的設(shè)計(jì)。運(yùn)行環(huán)境是什么并不是一個(gè)很好回到的問(wèn)題??梢詫⑵漕?lèi)比為我們的工作環(huán)境,當(dāng)我們需要辦公設(shè)備時(shí),只需要向管理部門(mén)申請(qǐng),并不需要關(guān)心辦公設(shè)備如何采購(gòu),辦公設(shè)備對(duì)一般的工作人員而言,就像是工作環(huán)境中天然存在的東西。

2、包管理的具體服務(wù)形式——Intent的解析:

在Android中,使用Intent來(lái)表達(dá)意圖,最終會(huì)有一個(gè)響應(yīng)者。當(dāng)系統(tǒng)產(chǎn)生一個(gè)Intent后,如何找到它的響應(yīng)者?這需要對(duì)Intent進(jìn)行解析。作為所有包信息管理者的中樞,PackageManagerService自然有義務(wù)承擔(dān)解析Intent的責(zé)任。要解析Intent,就需要了解Intent的結(jié)構(gòu),標(biāo)識(shí)了一個(gè)Intent身份的信息由兩部分構(gòu)成:

  • 主要信息:主要信息Action和Data。Action用于表明Intent所要執(zhí)行的操作,譬如ACTION_VIEW,ACTION_EDIT;Data用于表明執(zhí)行操作的數(shù)據(jù),譬如聯(lián)系人數(shù)據(jù),數(shù)據(jù)是以URI來(lái)表達(dá)的。再舉兩個(gè)Action和Data成對(duì)出現(xiàn)的例子:
    - ACTION_VIEW:content://contacts/people/1 :標(biāo)識(shí)查看聯(lián)系人數(shù)據(jù)庫(kù)中,ID為1的聯(lián)系人信息
    - ACTION_DIAL:tel:119 :表達(dá)撥打電話給119
    上面兩個(gè)例子的URI并不一樣,完整的URI格式為scheme://host:port/path。
  • 次要信息:除了主要標(biāo)記的信息,Intent還可以附加很多額外的信息,比如Category,Type,Componten和Extra:
    - Category:標(biāo)識(shí)Intent的類(lèi)別,譬如CATEGROY_LAUNCHER標(biāo)識(shí)對(duì)屬于左面的圖標(biāo)這一類(lèi)的對(duì)象執(zhí)行操作
    - Type:標(biāo)識(shí)Intent所有操作的數(shù)據(jù)類(lèi)型,就是MIMEType,譬如要操作PNG圖片,那Type就可以設(shè)置為png
    - Component:標(biāo)識(shí)Intent要操作的對(duì)象
    - Extra:標(biāo)識(shí)Intent所傳遞的數(shù)據(jù)
    上述這些數(shù)據(jù)都實(shí)現(xiàn)了Parcelable接口。

之所以Intent信息的主次之分,是因?yàn)榻馕鯥ntent的規(guī)則需要有一個(gè)依據(jù),主要信息是最能表達(dá)意圖的,而次要信息則是解析規(guī)則的一個(gè)補(bǔ)充。這就像大家在做自我介紹的時(shí)候,總是先說(shuō)姓名、籍貫這些主要的信息,再額外補(bǔ)充愛(ài)好、特長(zhǎng)這些次要信息,這樣一來(lái)在和其他人交朋友的時(shí)候,其他人就可以先根據(jù)籍貫、姓名鎖定我。如果我們只介紹愛(ài)好、特長(zhǎng),那么別人鎖定的范圍就比較廣,因?yàn)橛邢嗤瑦?ài)好或者特長(zhǎng)的人比較多。

Intent身份信息,其實(shí)就是Android的一種設(shè)計(jì)語(yǔ)言,譬如"打電話給119",只需要發(fā)出Action為ACTION_DIAL,URI為“tel:119”的Intent即可,剩下的就交給Android系統(tǒng)去理解這個(gè)意圖。任何組件只要按照規(guī)則發(fā)生,都會(huì)被Android系統(tǒng)正確的理解。

而根據(jù)Intent的方式不同,可以將Intent分為兩類(lèi):

  • 顯示(Explicit):明確指明需要誰(shuí)來(lái)響應(yīng)Intent。這一類(lèi)Intent的解析過(guò)程比較簡(jiǎn)單
  • 隱式(Implicit):有系統(tǒng)找出合適的目標(biāo)來(lái)響應(yīng)Intent。這一類(lèi)Intent的解析過(guò)程比較復(fù)雜,由于目標(biāo)明確,所以需要經(jīng)過(guò)層層篩選才能找到最合適的響應(yīng)者。

之所以Intent有顯式和隱式之分,是因?yàn)榻馕鯥ntent的方式不同,如果我指定要和某某交朋友,那么發(fā)出的這一類(lèi)請(qǐng)求,就是顯式Intent;如果沒(méi)有指定交朋友的對(duì)象,只是說(shuō)找到跟我愛(ài)好相同的人,那發(fā)出的這一類(lèi)請(qǐng)求,就是隱式的Intent。對(duì)待這兩種Intent顯然有不同的解析方式。

如果和"運(yùn)行環(huán)境Context"一樣,Intent也是面向應(yīng)用程序設(shè)計(jì),同樣是弱化了了進(jìn)程的概念。應(yīng)用程序只需表明"我想要什么",不需要關(guān)心索要的東西在什么地方,如何找到索要的東西。Intent是Android通信的手段之一,可以承載要傳遞的信息,至于信息怎么從發(fā)起進(jìn)程傳遞到目標(biāo)進(jìn)程,應(yīng)用程序可以毫不關(guān)心。

Intent最后一個(gè)響應(yīng)者是一個(gè)Android組件,Android組件都可以定義IntentFilter,前面說(shuō)了包解析器的時(shí)候,說(shuō)到了每一個(gè)Component類(lèi)中都有一個(gè)IntentInfo對(duì)象的數(shù)組,而IntentInfo則是IntentFilter的子類(lèi)。既然一個(gè)Android組件可以定義多個(gè)IntentFilter,那么Intent想要匹配到最終的組件,則需要通過(guò)組件所定義的所有IntentFilter:

IntentFilter.png

多個(gè)IntentFilter之間是"或"的關(guān)系,哪怕其他所有IntentFilter都匹配失敗,只要有一個(gè)IntentFilter通過(guò),最終Intent還是找到了可以響應(yīng)的組件。

每一個(gè)IntentFilter就像是一個(gè)定義了白名單規(guī)則的過(guò)濾器,只有滿足白名單的要求才會(huì)放行。Intent的過(guò)濾規(guī)則,其實(shí)就是針對(duì)Intent的身份信息的匹配規(guī)則,當(dāng)Intent的身份信息與IntentFilter所規(guī)定的要求匹配上,則允許通過(guò);否則,Intent就被過(guò)濾掉了。IntentFilter的過(guò)濾規(guī)則包含以下三個(gè)方面:

  • Action:每個(gè)IntentFilter可以定義零個(gè)或多個(gè)<action標(biāo)簽>,如果Intent想要通過(guò)這個(gè)IntentFilter,則Intent所轄的Action需要匹配其中至少一個(gè)。
  • Category:每一個(gè)IntentFilter可以定義零個(gè)或者多個(gè)<category>標(biāo)簽,如果Intent想要通過(guò)這個(gè)IntentFilter,則Intent所轄的Categroy必須是IntentFilter所定義的Category的子集,才能通過(guò)IntentFileter。譬如Intent設(shè)置了兩個(gè)Category:CATEGORY_LAUNCHER和CATEGORY_MAIN,只有那些至少定義了兩項(xiàng)Category的IntentFilter,才會(huì)放行該Intent。啟動(dòng)Activity時(shí),會(huì)為Intent設(shè)置默認(rèn)的Category,即CATEGORY_DEFAULT。目標(biāo)Activity需要添加一個(gè)category偽CATEGORY_DEFAULT的IntentFilter來(lái)匹配這一類(lèi)隱式的Intent。
  • Data:每一個(gè)IntentFilter可以定義零個(gè)或多個(gè)<data>,數(shù)據(jù)可以通過(guò)類(lèi)型(MIMEType)和位置(URI)來(lái)描述,如果Intent想要通過(guò)這個(gè)IntentFilter,則Intent所轄的Data需要匹配其中至少一個(gè)。

在了解Intent的身份信息和IntentFilter的規(guī)則定義之后,就可以介紹Intent解析的過(guò)程了,PackageManagerService有四大組件的Intent解析器,分別是ActivityIntentResolver用于解析發(fā)往Activity或Broadcast的Intent,ServiceIntentResolver用于解析發(fā)往Service的Intent,ProviderIntentResolver用于解析發(fā)往Provider的Intent,系統(tǒng)每收到一個(gè)Intent的解析請(qǐng)求時(shí),就會(huì)使用對(duì)應(yīng)的解析器,他們都是IntentResolver的子類(lèi)。

IntentResolver的職能就是解析Intent,它包含了所有IntentFilter,同時(shí)有一個(gè)重要的成員函數(shù)queryIntent(),接受Intent作為參數(shù),返回查詢(xún)結(jié)構(gòu):一個(gè)ResolveInfo對(duì)象的數(shù)據(jù)。因?yàn)榭赡苡卸鄠€(gè)組件來(lái)響應(yīng)一個(gè)Intent,所以返回結(jié)果是一個(gè)數(shù)組??上攵摵瘮?shù)就是針對(duì)輸入的Intent,按照前面所述的過(guò)濾規(guī)則,逐個(gè)與IntentFilter進(jìn)行匹配,直到找到最終的響應(yīng)者,便加入返回結(jié)果的列表。

  • ResolveInfo是最終的Intent解析結(jié)果的數(shù)據(jù)結(jié)構(gòu),并不復(fù)雜,就是各類(lèi)組件信息的一個(gè)包裝。需要注意的是,其中的組件信息ActivityInfo、ProviderInfo、ServiceInfo只有一個(gè)不為空,這樣就可以區(qū)分不同組件的解析結(jié)果。
  • 解析"顯式"的Intent,如果Intent中有設(shè)置Component,則說(shuō)明已經(jīng)顯式的指明由誰(shuí)來(lái)響應(yīng)Intent。根據(jù)Component可以獲取到對(duì)應(yīng)的 ActivityInfo,再封裝成ResolveInfo便可以作為結(jié)果返回了。
  • 解析"隱式"的Intent,該處邏輯比較復(fù)雜,后面講解Activity的啟動(dòng)流程時(shí)再詳細(xì)講解。

3、小結(jié)

  • 包管理對(duì)外提供服務(wù)的形式基于Bidner機(jī)制,服務(wù)端是運(yùn)行在系統(tǒng)進(jìn)程中的PackageManagerService,包查詢(xún)服務(wù)是使用范圍很廣的一類(lèi)服務(wù),很多其他服務(wù)都需要用到包信息,都是通過(guò)PackageManagerService獲取的。
  • 包查詢(xún)服務(wù)的核心是Intent解析,PackageManagerService中實(shí)現(xiàn)了不同組件的解析器。針對(duì)一個(gè)輸入的Intent,解析得到可以響應(yīng)的組件。Android為此設(shè)計(jì)了IntentFilter機(jī)制,定義了Intent匹配規(guī)則,最終解析實(shí)現(xiàn)在IntentResolver.queryIntent()函數(shù)中

五、PackageManagerService的體系結(jié)構(gòu)

image.png

大圖地址

六、總結(jié)

本片文章主要講解了包管理的三個(gè)大的過(guò)程:包掃描過(guò)程、包查詢(xún)過(guò)程、包安裝過(guò)程,其中重點(diǎn)的是包安裝的過(guò)程。我們?cè)賮?lái)復(fù)習(xí)一下:

APK的安裝流程如下:

復(fù)制APK安裝包到/data/app目錄下,解壓縮并掃描安裝包,向資源管理器注入APK資源,解析AndroidManifest文件,并在/data/data目錄下創(chuàng)建對(duì)應(yīng)的應(yīng)用數(shù)據(jù)目錄,然后針對(duì)Dalvik/ART環(huán)境優(yōu)化dex文件,保存到dalvik-cache目錄,將AndroidManifest文件解析出的組件、權(quán)限注冊(cè)到PackageManagerService并發(fā)送廣播

具體流程如下:

├── PMS.installPackage()
    └── PMS.installPackageAsUser()
         |傳遞 InstallParams 參數(shù)
        PackageHandler.doHandleMessage().INIT_COPY
         |
        PackageHandler.doHandleMessage().MCS_BOUND
         ├── HandlerParams.startCopy()
         │    ├── InstallParams.handleStartCopy()
         │    │    └──InstallArgs.copyApk()
         │    └── InstallParams.handleReturnCode()
         │         └── PMS.processPendingInstall()
         │              ├── InstallArgs.doPreInstall()
         │              ├── PMS.installPackageLI()
         │              │    ├── PackageParser.parsePackage()
         │              │    ├── PackageParser.collectCertificates()
         │              │    ├── PackageParser.collectManifestDigest()
         │              │    ├── PackageDexOptimizer.performDexOpt()
         │              │    ├── InstallArgs.doRename()
         │              │    │    └── InstallArgs.getNextCodePath()
         │              │    ├── replacePackageLI()
         │              │    │    ├── shouldCheckUpgradeKeySetLP()
         │              │    │    ├── compareSignatures()
         │              │    │    ├── replaceSystemPackageLI()
         │              │    │    │    ├── killApplication()
         │              │    │    │    ├── removePackageLI()
         │              │    │    │    ├── Settings.disableSystemPackageLPw()
         │              │    │    │    ├── createInstallArgsForExisting()
         │              │    │    │    ├── deleteCodeCacheDirsLI()
         │              │    │    │    ├── scanPackageLI()
         │              │    │    │    └── updateSettingsLI()
         │              │    │    └── replaceNonSystemPackageLI()
         │              │    │         ├── deletePackageLI()
         │              │    │         ├── deleteCodeCacheDirsLI()
         │              │    │         ├── scanPackageLI()
         │              │    │         └── updateSettingsLI()
         │              │    └── installNewPackageLI()
         │              │         ├── scanPackageLI()
         │              │         └── updateSettingsLI()
         │              ├── InstallArgs.doPostInstall()
         │              ├── BackupManager.restoreAtInstall()
         │              └── sendMessage(POST_INSTALL)
         │                   |
         │                  PackageHandler.doHandleMessage().POST_INSTALL
         │                   ├── grantRequestedRuntimePermissions()
         │                   ├── sendPackageBroadcast()
         │                   └── IPackageInstallObserver.onPackageInstalled()
         └── PackageHandler.doHandleMessage().MCS_UNBIND
              └── PackageHandler.disconnectService()

至此,整個(gè)APK安裝流程詳解全部說(shuō)完,謝謝!

官人[飛吻],你都把臣妾從頭看到尾了,喜歡就點(diǎn)個(gè)贊唄(眉眼)?。。。?/h2>

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

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