如果你是Android 系统中的架构师,让你设计一个Android的安装系统中的PackageManagerService,你会怎么设计? 既然要设计,咱们要首先弄清几个问题,我希望大家看下面的问题的时候,多想两个问题:1、如果让你设计,你怎么设计。这个"类"存在意义是什么?
那我们就来依次来看下这几个问题
其实上面的解答是基本上所有操作的系统的安装思路,大家可以想一下在Windows下是不是也是如此。
上面说的Android区域其实就是:“/data目录”下的system目录,这个目录用来保存很多系统文件。主要工作是创建了5个位于目录/data/system的File对象,分别是:
这个5个文件中pakcages-back.xml和pakcages-stoped-backup.xml是备份文件。当Android对文件packages.xml和pakcages-stoped.xml写之前,会先把它们备份,如果写文件成功了,再把备份文件删除。如果写的时候,系统出问题了,重启后在需要读取这两个文件时,如果发现备份文件存在,会使用备份文件的内容,因为源文件可能已经损坏了。其中packages.xml是PackageManagerServcie启动时,需要用到的文件。 我把我的Nexus 6P手机Root后,在/data/system 截屏如下:
/data/system目录.png
我把packages.xml导出来,文件内容太大,我就直接截屏了,内容如下:
截屏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>
上面是我手机packages.xml的一个片段。我们看下里面的"youtube"应用。通过标签<package>记录了一个应用的基本信息,签名和声明的权限。
通过上面的内容,我们知道Android系统通过packages.xml文件来存储应用信息的,所以我们举一反三,新安装的APK,肯定是把新安装的APK相关信息写入这个packages.xml文件中,那么怎么把这个xml文件,映射到内存中的? 那我们就来看第二个问题
上面提到了,应用的信息都存储在packages.xml中的<package>标签里面,那我们是怎么加载到内存中去的?大家平时是存储数据库的时候都是怎么做的?对的,一般都是一个实体类对应数据库中的一个表;其中每一个对象对应的是数据库中的一条数据。同理,Android系统也是这样设计的,<package>标签里面记录的包信息其实是一一对应的PackageSetting类。
PackageSetting类的继承关系.png
PackageSetting继承了PackageSettingBase类,PackageSettingBase类继承自GrantedPremisson类。应用的基本信息保存在PackageSettingBase类的成员变量中,声明的权限保存在GrantedPremissions类,签名则保存在SharedUserSetting类的成员变量signatures中。标签<package>所标识的应用PackageSetting对象都保存在Setting的mPackages中,定义如下:
// com.android.server.pm.Settings.java
final HashMap<String, PackageSetting> mPackages =
new HashMap<String, PackageSetting>();
在packages.xml中除了标签<package>,还有<updated-package>、<cleaning-package>和<renamed-package> 这三种标签。
其中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>();
上面用大量的文笔说Settings,那么它是什么东西?下面就让我继续来看下一个问题
这个类就是Settings
Settings是Android的包的全局管理者,用于协助PackageManagerService保存所有的安装包信息,同时用于存储系统执行过程中的一些设置,PackageManagerService和Settings之间的类图关系如下:
PackageManagerService和Settings的关系.png
Settings里面有3个重要的成员变量: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>();
其中ShareUserSetting类继承自GrantedPermissions ,内部包含一个ArraySet类型的packages ,这个packages保存了声明相同的shareUserId的Package的权限设置信息(PackageSetting )通过上面的问题,我们知道PackageSetting继承自PackageSettingBase,同时PackageSetting中保存着package的多种信息。
如下图:
PackageSetting.png
上面提到了一个概念是SharedUserSetting,那么ShareUserSetting的作用什么是什么?那我们就来看下:
SharedUserSetting用来描述具有相同的sharedUserId的应用信息,它的成员变量packages保存了所有具有相同sharedUserId的应用信息引用。这些应用的签名时相同的,所有只需要在成员变量signatures中保存一份。通过这个对象,Android运行时很容易检索到某个应用拥有相同的sharedUserId的其他应用。其中应用的签名保存在ShardUserSetting类的成员变量signatures中。
我们在看系统应用的AndroidManifest.xml中会发现
<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相关,作用是:
在Settings中和用户有关的还有两个重要变量,即mUserIds 和mOtherUserIds
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
private final SparseArray<Object> mOtherUserIds =
new SparseArray<Object>();
他们都是以UID为索引,得到对应的ShardUserSetting对象。更多的关于Android系统中关于"用户"的信息,在后面"用户模块"再单独讲解
在Android中,Settings提供可持续化的包信息管理,PackageSetting是一个存储单元,表示一个pkg信息。我们在解析APK安装包的时候,会用到PackageParser,在PackageParser里面有一个字段是PackageParse.Package。这个PackageParse.Package其实是对应的上面packages.xml里面的<package>标签。同时PackageParse.Package也可以理解为pkg信息在内存中的一个实时信息,关机后变消失,重启后重新生成,所以PackageParse.Package中的信息一致保证最新。PackageParse.Packag、Settings和PackageSetting三者的关系如下:
关系.png
Settings中保存了一个包名和PackagesSetting的映射表,PackageParse.Package中的mExtras引用指向了对应的PackageSetting实例,而PackageParse中保存了一个PackageParse.Package列表
PackageParse.png
从上到下,介绍如下: 1:PackageParser.Package对应一个apk完整的原始数据 2:PackageSetting包含一个PackageParser对象实例,说明它也对应一个apk包的数据,不同的是,它还包括apk相关配置数据,比如apk内部哪些component是被disable等。 3:Settings包含了PackageSetting对象列表,也就是说它包含了系统所有apk数据,还有就是PackageParser,顾名思义,负责APK数据解析 4:PackageManagerService是全局的包管理器
Settings里面的主要关联关系如下图:
主要关联关系.png
上面说了很多,我们再上升一个高度,PackageManagerService到底应该怎么去理解它?
每一个组织结构,都有一套自己的管理机制,比如任何一家公司,都会存在下面三个元素:管理者(经理)、被管理者(员工)、管理机制(公司的规章制度及KPI考核等)。同理在Android的系统的世界里面,也有一家公司叫"包管理"。如果要研究Android的包管理机制,同样可以从以下几个角度来思考?
所谓包,其实就是一种文件的格式,比如APK包,JAR包等,在Android中存活着很多包,所有的应用程序都是APK包,很多构成Android运行环境的都是JAR包,还有一些以so为后缀的库文件,包管理者很重要的一个职责就是识别不同的包,统一维护这些包的信息。当有一个包进入(安装)或者离开(卸载)Android世界,都需要向包管理者申报,其他管理部分要获取包的具体信息,也都需要向包管理者申请。
如同一家公司是由人与人协作工作的,不同包之间也需要进行协作。既然有协作,自然就有协作的规范,一个包可以干什么,不可以干什么,都需要有一个明确的范围界定,这就是包管理中的权限设计。涉及到的内容非常广泛,Android的权限管理、SELinux,都是包管理中权限设计的组成部分。同理Android的世界就像一个井然有序的一家公司,既有包管理部门,也有其他各种管理部门,比如电源管理部门,窗口管理部门等等。大家不仅各司其职,而且也有来往。比如在APK安装到Activity的显示,看着很简单的过程,其实却需要大量的管理部门参与进来,不断地进行数据解析、封装、传递、呈现,其内部机制十分复杂。
现在大家想一下上面三个问题的答案,我详细大部分人的前两个答案是一致的,管理者是PackageManagerService,被管理是各种"包",最后一个答案是各有千秋,这里是没有标准答案的,希望大家能自己找到自己的答案。
PackageManagerService涉及的数据结构非常多,在分析源码时,很容易陷入各种数据结构之间的关系,难以自拔,以至于看不到包管理的全貌。我在这里简单的总结了一下各个数据结构的职能如下:
—————————————分隔符—————————————
PackageParser的数据结构.png
—————————————分隔符—————————————
数据结构.png
如果大家想对Android系统有一个大致的了解,就必须要要了解PackageManagerService的三大流程
下面我们来一一进行简单的介绍
扫描目录的目的:
扫描Android系统的几个目标文件中的APK,从而建立合适的数据结构以及管理诸如Package信息、四大组件、授权信息等各种信息。抽象的地看,PackageManagerService像一个工厂,它解析实际的物理文件(APK文件),以及生成符合自己要求的产品。比如PackageManagerService将解析APK包中的AndroidManifest.xml,并根据其中声明的Activity标签来创建与此对应的对象,并保存到PackageParser.Package类型的变量中,然后通过PackageManagerService的scanPackageDirtyLI()方法将解析后的组件数据统计到PackageManagerService的本地变量中,用于管理查询调用,当系统中任意某个APK的package发生改变时,如卸载,升级等操作都会更新package的统计数据到PackageManagerService,PackageManagerService正式基于拥有系统中所有的Package的信息才能胜任"包管理"这个管理者的角色。PackageManagerService的工作流程相对简单,复杂的是其中用于保存各种信息的数据结构和它们的关联关系,以及对应影响结果的策略控制(比如系统应用和普通应用)
如果把包扫描过程组看成一件事,那么这件事就是:
调用PackageManagerService类的静态方法main()方法来获取PackageManagerService对象
如果把包扫描过程组看成两件事,那么这两件事就是
1、创建PackageManagerService对象 2、将PackageManagerService向ServiceManager注册,即加入SMS,方便后续其他进程或者app通过ServiceManager获得PackageManagerService服务。
如果把包扫描过程组看成三件事,那么这三件事是:
1、先读取保存在packages.xml中记录的系统关机前记录所有安装的APP信息, 将其保存在PackageManagerServiced中mSettings中的mPackages中。 2、扫描指定的若干目录中的app,并把信息记录在PackageManagerServiced的mPackages中。 3、最后上面的两者进行对比,看是否有升级的APP,然后进行相关处理,最后写入package.xml中
当然换一个角度,以扫描角度来看,也可以把包扫描分解成另外三个阶段:
如果把包扫描过程组看成四件事,那么这四件事是:
1、读取响应的配置文件 2、优化APK和Jar包 3、扫描系统中所有安装的应用 4、把扫描出的所有应用信息进行保存
如果把包扫描过程组划分的更细,则我将其分为6大步骤
如果把包扫描过程组划分的更细,则我将其分为9大步 第一步:创建Settings对象,并调用其addSharedUserLPw()方法,保存ShareUserSetting信息 第二步:创建Installer对象,用于Native进程installd交互 第三步:创建ThreadHandler线程,并以其Looper为参数创建PackageHandler对象,用于程序的安装和卸载 第四步:根据Installer对象和/data/user文件对象创建UserManager对象,用于多用户管理 第五步:调用readPermissions()方法,从/system/etc/permissions目录下的XML文件读取权限信息 第六步:调用Settings对象的readLPw()方法解析/data/system目录下的文件: 第七步:扫描/system/frameworks目录以及BOOTCLASSPATH和platform.xml定义的系统目录下的jar和APK文件是否需要dex优化,如果需要则调用Installer.dexopt()方法来发送消息给installd让它优化;如果任意一个文件执行了dex优化操作,删除/data/dalvik-cache目录下的缓存文件 第八步:创建AppDirObserver对象监听/system/frameworks、/system/app、/vendor/app(厂商定制)、/data/app、/data/app-private5个目录,并调用scanDirLI()方法扫描其中的APK文件: 第九步:汇总上面扫描XML和APK得到的信息,并写入文件;
开机扫描流程.png
安装一个APK的其大致流程如下:
大致流程.png
通常,安装一个APK 通常分为以下4种方式
下面我们就依次介绍下
系统的应用的安装主要在PackageManagerService的main方法里面进行操作的
其顺序如下: 第一步:PackageManagerService.main()初始化注册 第二步:建立Java层的installer与C层的intalld的socket联接 第三步:建立PackageHandler消息循环 第四步:调用成员变量mSettings的readLPw()方法恢复上一次的安装信息 第五步:.jar文件的detopt优化 第六步:scanDirLI函数扫描特定目录的APK文件解析 第七步:updatePermissionsLPw()函数分配权限 第八步:调用mSettings.writeLPr()保存安装信息
其顺序如下: 第一步:调用PackageManagerService的installPackage方法 第二步:上面的方法调用installPackageWithVerfication(),进行权限校验,发送INIT_COPY的msg 第三步:进入PackageManagerService的doHandleMessage方法的INIT_COPY分支 第四步:成功绑定了com.android.defcontainer.DefaultContainerService服务,进入MCS_BOUND分支 第五步:里面调用PackageManagerService中内部抽象类HandlerParams的子类InstallParams的startCopy方法。 第六步:抽象类的HandlerParams的startCopy方法调用了HandlerParams子类的handleStartCopy和handlerReturnCode两个方法 第七步:handlesStartCopy方法调用了InstallArgs的子类copyApk,它负责将下载的APK文件copy到/data/app 第八步:handleReturnCode调用handleReturnCode方法 第九步:调用PackageManagerService服务的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法进行APK扫描。 第十步:上面的方法判断是否APP应安装,调用installNewPackageLI或replacePackageLI方法 第十一步:调用updateSettingsLI方法进行更新PackageManagerService的Settings 第十二步:发送what值为POST_INSTALL的Message给PackageHandler进行处理 第十三步:发送what值为MCS_UNBIND的Message给PackageHandler,进而调用PackageHandler.disconnectService()中断连接
Android Debug Bridge (adb)是SDK自带的管理设备的工具,通过ADB命令的方式也可以为手机或者模拟器安装应用,其入口函数为pm.java
Android Debug Bridge (adb) 是SDK自带的管理设备的工具,通过ADB命令行的方式也可以为手机或模拟器安装应用,其入口函数源文件为pm.java
其顺序如下: 第一步:pm.java的runInstall()方法 第二步:参数不对会调用showUsage方法,弹出使用说明 第三步:正常情况runInstall会调用mPm变量的installPackageWithVerification方法 第四步:由于pm.java中的变量mPm是PackageManagerService的实例,所以实际上是调用PackageManagerService的installPackageWithVerfication()方法 第五步:进入PackageManagerService的doHandleMessage方法的INIT_COPY分支 第六步:成功绑定了com.android.defcontainer.DefaultContainerService服务,进入MCS_BOUND分支 第七步:里面调用PackageManagerService中内部抽象类HandlerParams的子类InstallParams的startCopy方法。 第八步:抽象类的HandlerParams的startCopy方法调用了HandlerParams子类的handleStartCopy和handlerReturnCode两个方法 第九步:handlesStartCopy方法调用了InstallArgs的子类copyApk,它负责将下载的APK文件copy到/data/app 第十步:handleReturnCode调用handleReturnCode方法 第十一步:调用PackageManagerService服务的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法进行APK扫描。 第十二步:上面的方法判断是否APP应安装,调用installNewPackageLI或replacePackageLI方法 第十三步:调用updateSettingsLI方法进行更新PackageManagerService的Settings 第十四步:发送what值为POST_INSTALL的Message给PackageHandler进行处理 第十五步:发送what值为MCS_UNBIND的Message给PackageHandler,进而调用PackageHandler.disconnectService()中断连接
ADB安装.png
第一步:调用PackageInstallerActivity的onCreate方法初始化安装界面 第二步:初始化界面以后调用initiateInstall方法 第三步:上面的方法调用startInstallConfirm方法,弹出确认和取消安装的按钮 第四步:点击确认按钮,打开新的activity:InstallAppProgress 第五步:InstallAppProgress类初始化带有进度条的界面之后,调用PackageManager的installPackage方法 第六步:PackageManager是PackageManagerService实例,所以就是调用PackageManagerService的installPackage方法 第七步:调用PackageManagerService的installPackage方法 第八步:上面的方法调用installPackageWithVerfication(),进行权限校验,发送INIT_COPY的msg 第九步:进入PackageManagerService的doHandleMessage方法的INIT_COPY分支 第十步:成功绑定了com.android.defcontainer.DefaultContainerService服务,进入MCS_BOUND分支 第十一步:里面调用PackageManagerService中内部抽象类HandlerParams的子类InstallParams的startCopy方法。 第十二步:抽象类的HandlerParams的startCopy方法调用了HandlerParams子类的handleStartCopy和handlerReturnCode两个方法 第十三步:handlesStartCopy方法调用了InstallArgs的子类copyApk,它负责将下载的APK文件copy到/data/app 第十四步:handleReturnCode调用handleReturnCode方法 第十五步:调用PackageManagerService服务的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法进行APK扫描。 第十六步:上面的方法判断是否APP应安装,调用installNewPackageLI或replacePackageLI方法 第十七步:调用updateSettingsLI方法进行更新PackageManagerService的Settings 第十八步:发送what值为POST_INSTALL的Message给PackageHandler进行处理 第十九步:发送what值为MCS_UNBIND的Message给PackageHandler,进而调用PackageHandler.disconnectService()中断连接
流程.png
小结:
在写应用程序时,我们通常会利用应用自身的上下文环境Context来获取包管理服务,如下:
// 获取一个PackageManager的对象实例
PackageManager pm = context.getPackageManager();
// 通过PackageManager对象获取指定包名的包信息
PackageInfo pi = pm.getPackageInfo("com.android.contacts", 0);
这么一段简单的代码,其实蕴含很多的深意
IPackageManager.png
PackageManagerService作为包管理的最核心组成部分,伴随着系统的启动而创建,并一直运行系统进程中。当应用程序需要获取包管理服务时,会生成一个PackageManager对PackageManagerService进行通信。在包解析时就会生成包信息,即XXInfo这一类数据结构,PackageManagerService将这些数据传递给需要的应用进程。
管理者对内设计了复杂的管理机制,对外封装了简单的使用接口。这种设计在Android中大量出现,比如ActivityManagerService、WindowManagerService、PowerManagerService等,基本所有的系统服务都遵循这种设计规范。对于应用程序而言,不需关心管理者的实现原理,只需要理解接口的使用场景
Android在全局定义了IPackageManager,接口,描述了包管理者对外提供的功能,运行在系统进程中的PackageManagerService实现了IPackageManager接口,作为包管理的服务端,客户端通过IPackageManager接口请求包服务。为了方便客户端进行包服务,Android做了多层的封装。应用进程作为客户端,通过PackageManager便可使用包服务,客户端实际存在的对象是ApplicationPackageManager,它封装了IPackageManager的所有接口。在应用进程来看,客户端和服务端的概念是模糊的,明确的只有运行环境的概念,即Context。包服务就存在于应用进程的运行环境中,需要时直接拿出来使用即可。
“运行环境(Context)”是Android的设计哲学之一,Android有意弱化进程,强化运行环境,这是面向应用开发者的设计。运行环境是什么并不是一个很好回到的问题。可以将其类比为我们的工作环境,当我们需要办公设备时,只需要向管理部门申请,并不需要关心办公设备如何采购,办公设备对一般的工作人员而言,就像是工作环境中天然存在的东西。
在Android中,使用Intent来表达意图,最终会有一个响应者。当系统产生一个Intent后,如何找到它的响应者?这需要对Intent进行解析。作为所有包信息管理者的中枢,PackageManagerService自然有义务承担解析Intent的责任。要解析Intent,就需要了解Intent的结构,标识了一个Intent身份的信息由两部分构成:
之所以Intent信息的主次之分,是因为解析Intent的规则需要有一个依据,主要信息是最能表达意图的,而次要信息则是解析规则的一个补充。这就像大家在做自我介绍的时候,总是先说姓名、籍贯这些主要的信息,再额外补充爱好、特长这些次要信息,这样一来在和其他人交朋友的时候,其他人就可以先根据籍贯、姓名锁定我。如果我们只介绍爱好、特长,那么别人锁定的范围就比较广,因为有相同爱好或者特长的人比较多。
Intent身份信息,其实就是Android的一种设计语言,譬如"打电话给119",只需要发出Action为ACTION_DIAL,URI为“tel:119”的Intent即可,剩下的就交给Android系统去理解这个意图。任何组件只要按照规则发生,都会被Android系统正确的理解。
而根据Intent的方式不同,可以将Intent分为两类:
之所以Intent有显式和隐式之分,是因为解析Intent的方式不同,如果我指定要和某某交朋友,那么发出的这一类请求,就是显式Intent;如果没有指定交朋友的对象,只是说找到跟我爱好相同的人,那发出的这一类请求,就是隐式的Intent。对待这两种Intent显然有不同的解析方式。
如果和"运行环境Context"一样,Intent也是面向应用程序设计,同样是弱化了了进程的概念。应用程序只需表明"我想要什么",不需要关心索要的东西在什么地方,如何找到索要的东西。Intent是Android通信的手段之一,可以承载要传递的信息,至于信息怎么从发起进程传递到目标进程,应用程序可以毫不关心。
Intent最后一个响应者是一个Android组件,Android组件都可以定义IntentFilter,前面说了包解析器的时候,说到了每一个Component类中都有一个IntentInfo对象的数组,而IntentInfo则是IntentFilter的子类。既然一个Android组件可以定义多个IntentFilter,那么Intent想要匹配到最终的组件,则需要通过组件所定义的所有IntentFilter:
IntentFilter.png
多个IntentFilter之间是"或"的关系,哪怕其他所有IntentFilter都匹配失败,只要有一个IntentFilter通过,最终Intent还是找到了可以响应的组件。
每一个IntentFilter就像是一个定义了白名单规则的过滤器,只有满足白名单的要求才会放行。Intent的过滤规则,其实就是针对Intent的身份信息的匹配规则,当Intent的身份信息与IntentFilter所规定的要求匹配上,则允许通过;否则,Intent就被过滤掉了。IntentFilter的过滤规则包含以下三个方面:
在了解Intent的身份信息和IntentFilter的规则定义之后,就可以介绍Intent解析的过程了,PackageManagerService有四大组件的Intent解析器,分别是ActivityIntentResolver用于解析发往Activity或Broadcast的Intent,ServiceIntentResolver用于解析发往Service的Intent,ProviderIntentResolver用于解析发往Provider的Intent,系统每收到一个Intent的解析请求时,就会使用对应的解析器,他们都是IntentResolver的子类。
IntentResolver的职能就是解析Intent,它包含了所有IntentFilter,同时有一个重要的成员函数queryIntent(),接受Intent作为参数,返回查询结构:一个ResolveInfo对象的数据。因为可能有多个组件来响应一个Intent,所以返回结果是一个数组。可想而知,该函数就是针对输入的Intent,按照前面所述的过滤规则,逐个与IntentFilter进行匹配,直到找到最终的响应者,便加入返回结果的列表。
image.png
本片文章主要讲解了包管理的三个大的过程:包扫描过程、包查询过程、包安装过程,其中重点的是包安装的过程。我们再来复习一下:
APK的安装流程如下:
复制APK安装包到/data/app目录下,解压缩并扫描安装包,向资源管理器注入APK资源,解析AndroidManifest文件,并在/data/data目录下创建对应的应用数据目录,然后针对Dalvik/ART环境优化dex文件,保存到dalvik-cache目录,将AndroidManifest文件解析出的组件、权限注册到PackageManagerService并发送广播
具体流程如下:
├── PMS.installPackage()
└── PMS.installPackageAsUser()
|传递 InstallParams 参数
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()
至此,整个APK安装流程详解全部说完,谢谢!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有