概述
前面几篇,通过分析View的绘制过程以及Activity的启动过程,了解了 Android系统中两个比较重要的系统服务 ActivityManagerService
和 WindowManagerService
,简称 AMS
, WMS
。
本文将详解负责App安装的另一个重要服务 PackageManagerService
本文涉及的代码都基于android-28
apk的打包过程
一个完整的安卓项目是由多个module构成,每个module中的内容大致可以分为两大类
- Resources 资源
- java/kotlin 代码
所以打包过程也基本上围绕这两个部分。
资源部分
资源文件包含 res目录下的 各种XML文件,动画,drawable ,音视频等。
负责打包资源文件的工具叫做 AAPT
,它是安卓SDK的一个部分。
XML文件会被编译成二进制。
drawable 和 mipmap 中的 JPG ,PNG 等 图片会保留。这也是我们可以解压别人的apk,取得其中图片资源的原因。
assets 和 raw 中的文件会保留原样,打包到apk内部。
我们最需要关注的是,XML文件中的各种 资源ID 和 它对应的资源 的配对关系。比如,color,string,layout,他们都通过以下两个文件进行管理:
- resource.arsc 文件 (资源索引表)
- R.java 文件
比如这个R.java文件 其中记录了很多资源ID,应用程序运行时,系统会通过这些id,去 resource.arsc 资源索引表中去查找对应的资源来加载。
代码部分
源代码文件会先通过javac
编译成 .class
文件,然后这些class
,会连同第三方库中的class
,一同被 dx
命令打包成dex
文件,如果有分包,那么也有可能生成多个dex
。 源代码部分也包含 AIDL 生成的 java文件,AIDL
会先生成java
文件然后生成class
,最后一同进入到 dex
。
打包阶段
当 资源和代码都被打包好了之后,APK Builder 会将经过编译的 resource.arsc 和 dex一起打包到 apk中。
除了代码和资源之外,还有清单文件 AndroidManifest.xml 和 第三方库中使用的so文件。
apk文件创建好了之后,还要使用签名工具 jarsigner 对其进行签名。
签名之后,会生成MEAT_INF目录,其中包含了 签名相关的各个文件。(这里只针对了v1签名,v2,v3签名是对整个apk字节码进行签名,所以没有明显产物)
MEAT_INF 中内容如下:
- CERT.SF 每个文件的相对密钥
- MANIFEST.MF 每个文件的数字签名信息
- XXX.SF JAR文件的签名文件
- XXX.RSA 对输出文件的签名和公钥
实际的打包后可能还有一个优化阶段,那就是使用 apk对齐工具,对未压缩的图片视频等资源进行对齐操作。
让资源按照4个字节的边界进行对齐,可以加快 资源的访问速度。
如果每个资源的开始位置都是上个资源之后的4n个字节,那么访问下一个资源就不用遍历,直接跳到4n字节处判断是不是新的资源即可。
到这里,一个完整的apk就打包完毕。
完整内容如下:
apk的安装过程
当我们在安卓系统上发起某个apk的安装流程时,首先会先看到一个系统的应用安装界面。 这个界面其实是,AndroidFramework层的 PackageInstallerActivity.java
点击安装之后,PackageInstallerActivity 会先把所安装的apk信息 通过 PackageInstallerSession 交给 PackageManagerService 处理。源代码如下所示,mPm就是 PackageManagerService的实例 :
整个安装过程分为两个步骤:
- 拷贝安装包
- 装载代码
拷贝安装包
下面将追踪 PackageManagerService
的 源代码来分析 拷贝过程。
图中1处,创建了一个Handler体系中的 Message
并指定了INIT_COPY
这个行为,
图中2处,用安装包的相关数据构建了一个 InstallParams
.
最后通过 mHandler将 消息发送出去。
消息的执行如下:
图中1处,获取了刚才构建的 InstallParams
图中2处,尝试连接安装apk的Service服务。
实际绑定的Servic就是上图中的DEFAULT_CONTAINER_COMPNENT
,
当绑定成功之后,将通过handler发送一个MCS_BOUND
消息,
消息的执行如下: 其中,从 mPnedingInstalls这个等待队列中取出刚才创建的 InstallParams 对象,并且一旦这个InstallParams开始拷贝,就从等待队列中移除它。
startCopy的源码如下,这是apk拷贝的核心方法:
图中1处,决定 安装的位置,是在手机内部存储空间,还是外部存储SD卡。
图中2处,判断apk安装位置,如果安装位置合法,则执行3处逻辑。
图中3处,创建出一个 InstallArgs
,并执行它的copyApk
方法。实际执行的代码在 FileInstallArgs
中。 并实际执行到了 doCopyApk。 doCopyApk 主要做了3件事。
1处,创建存储apk的目标路径。实际上是 data/app/[包名]/
这个目录 2处,将原始文件拷贝到目标路径下 3处,将apk的动态库 也拷贝到目标路径下。
继续跟踪上图中的 copyPacakge
方法,就走到了 刚才 connectToService
连接到的 DefaultContainerService
可以看出,拷贝的过程就是很简单的IO操作。并且写死的 apk文件的存储名称为base.apk
.
装载代码
apk拷贝完成之后,马上就进入到了 装载代码的流程。
回到 HandlerParams
的 startCopy方法。 装载的过程其实就是 processPendingInstall
方法。
方法内容如下:
图中1处,执行 预安装操作,主要是检查安装包的状态,确保安装环境正常。如果安装环境异常,则会清理拷贝文件。
图中2处,是真正的安装阶段。
图中3处,处理安装完成之后的操作。
下面详细关注 真正的安装阶段:
图中1处,解析 manifest清单文件。我们在清单文件中声明的四大组件,就是在这阶段注册到 系统中。注册之后,就可以通过 startActivity或者 startService的方式来启动相应的服务。
图中2处,对apk中的签名信息进行验证操作。如果相同的包名,我们已经安装了一个debug签名的,然后又安装一个release签名,两个签名不一样,就会中止安装。
图中3处,执行 DEX TO OAT
操作,将dex
转化成oat文件
。 图中4处,执行新apk的安装操作。具体代码如下:
图中 scanPackageLi 继续扫描apk中的文件,保存 apk相关信息到 pms中。并创建apk的data目录。
图中 updateSettingsLI 如果安装成功,更新系统中的应用信息,比如 应用的权限信息。
图中 deletePackageLi, 则是安装失败时,清空各种缓存文件,以及安装包。
安装完成之后,还会发送一个 App 安装成功的广播,ACTION_PACAKGE_ADDED , Launcher(手机桌面)注册了这个广播,当它收到这个广播之后,就会创建一个icon显示在桌面上。
总结
本篇主要介绍了 App的打包以及安装过程。
打包的主要内容是 资源和代码。生成apk之后还要签名,对齐优化。
apk的安装则分为 拷贝安装包,以及 代码装载两个过程。