Android中APK安装流程解析

前言:大家都知道,手机关机以后,就是一个冰冷的砖头,只能用来做防身的利器,但是开机后,点击桌面上的任何一个图片,都能开启一个APP,这说明在开机过程中,系统把已经安装好的APP加载到内存中,这到底是怎么做的?

所以我们可以推断,在安卓系统中肯定存在这么一块区域,用于存放已经安装的APP的信息,在开机的时候,通过系统扫描,这块区域,把对应的内容加载到内存中去。

其次,我们知道了在Android系统中存在这样一块区域,在开机的的时候,加载这块区域的信息,从而实现加载在内存中去。那么我们继续反推断,那这块区域的信息,是怎么来的?

应该在安装这个APK的时候,把这个APK的信息写入到该区域的。这样就可以实现了在安卓系统一次安装后,在删除APK文件后,还可以运行APP了。

该篇文章是讲述apk的安装流程,想要了解apk打包、app启动 流程的同学可以点击下面的传送门:

apk打包流程

app启动流程详解

apk信息存储在哪?

回到apk的安装话题上来,上面说的Android区域其实就是:“/data目录”下的system目录,这个目录用来保存很多系统文件。主要工作是创建了5个位于目录/data/system的File对象,分别是:

  • packages.xml:记录了系统中所有安装的应用信息,包括基本信息、签名和权限。
  • pakcages-back.xml:packages.xml文件的备份。
  • pakcages-stoped.xml:记录系统中被强制停止的运行的应用信息,系统在强制停止某个应用的时候,会将应用的信息记录在该文件中。
  • pakcages-stoped-backup.xml:pakcages-stoped.xml文件的备份。
  • packages.list:保存普通应用的数据目录和uid等信息。

这5个文件中pakcages-back.xml和pakcages-stoped-backup.xml是备份文件。当Android对文件packages.xml和pakcages-stoped.xml写之前,会先把它们备份,如果写文件成功了,再把备份文件删除。如果写的时候,系统出问题了,重启后在需要读取这两个文件时,如果发现备份文件存在,会使用备份文件的内容,因为源文件可能已经损坏了。其中packages.xmlPackageManagerServcie启动时,需要用到的文件。

为了更直观的看一下,下面copy一张手机Root后,在/data/system目录下 截图如下:

把packages.xml导出来,因为文件内容太大,所以展示局部内容如下:

<?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>

对上面的标签的含义,下面做一个简单的介绍:

<package>表示包信息,下面我们就来解释下标签<package>中的属性:

  • name表示应用的包名。
  • codePath表示的是apk文件的路径。
  • nativeLibraryPath表示应用的native库的存储路径。
  • flags是指应用的属性,如FLAG_SYSTEM、FLAG_PERSISTENT等。
  • it表示应用安装的时间。
  • ut表示应用最后一次修改的时间。
  • version表示应用的版本号。
  • userId表示所属于的id。

<sign>表示应用的签名,下面我们就来解释下 标签<sign>中的属性:

  • count表示标签中包含有多少个证书
  • cert表示具体的证书的值

<perms>表示应用声明使用的权限,每一个子标签代表一项权限。

在上面知道了apk的信息在系统的存储的位置和方式,有人就会有新的疑问:系统是怎么获取到apk的信息的?接下来就是要梳理apk的安装流程了,看了apk安装流程的解析,你就会知道apk的信息是怎么被系统获取到的了。

从源码角度分析apk安装过程

先介绍一下Apk安装的四种方式:

1. 系统应用安装:没有安装界面,在开机时自动完成。
2. 网络下载应用安装:  没有安装界面,在应用市场完成。
3. ADB命令安装:  没有安装界面,通过命令直接安装。
4. 外部设备安装:  有安装界面,通过SD卡等外部设备安装,由packageInstaller处理安装逻辑。

接下来介绍一下APK安装涉及到的几个常用目录:

1.system/app : 系统自带的应用程序,获得root权限才能删除。

2.data/app : 用户程序安装目录,安装时会把apk文件复制到此目录下。

3.data/data : 存放应用程序的数据。

4.data/dalvik-cache : 将apk中的dex文件安装到该目录下(dex文件是dalvik虚拟机的可执行文件,大小约为原始apk的四分之一)。

还有APK安装的预备知识点也说一下吧:

(1)PackageManagerService是由SystemServer启动,PMS负责应用的安装、卸载、权限检查等工作;

(2)在/system/app和/data/app目录下的apk文件,PMS在启动过程中,都会扫描安装;

(3)每次开机时,PMS都会在构造函数中对指定目录下的apk进行扫描,没有安装的apk就会触发安装;


在梳理源码之前先大概说一下apk安装的四大步骤:

(1)拷贝apk到指定的目录:默认情况下,用户安装的apk首先会拷贝到/data/app下,用户有访问/data/app目录的权限,但系统出厂的apk文件会被放到/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等,该分区需要root权限的用户才能访问。
(2)加载apk、拷贝文件、创建应用的数据目录:
为了加快APP的启动速度,apk在安装的时候,会首先将APP的可执行文件(dex)拷贝到/data/dalvik-cache目录下,缓存起来。再在/data/data/目录下创建应用程序的数据目录(以应用包名命令),用来存放应用的数据库、xml文件、cache、二进制的so动态库等。
(3)解析apk的AndroidManifest.xml文件:
在安装apk的过程中,会解析apk的AndroidManifest.xml文件,将apk的权限、应用包名、apk的安装位置、版本、userID等重要信息保存在/data/system/packages.xml文件中。这些操作都是在PackageManagerService中完成
的。
(4)显示icon图标:
应用程序经过PMS中的逻辑处理后,相当于已经注册好了,如果想要在Android桌面上看到icon图标,则需要Launcher将系统中已经安装的程序展现在桌面上。

概念说的差不多了,接下来就对不同的安装方式做源码的过程梳理。

源码解析系统应用安装

系统在创建PackageManagerService实例时,会在PMS的构造函数中开始执行安装应用程序的逻辑。

在PMS的构造函数中做了如下几点重要操作:

1.创建Settings对象,添加shareUserId。

    mSettings = new Settings(mPackages);
    mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
            ApplicationInfo.FL
  • 16
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值