【android】应用程序安装过程分析(二)——————系统正常运行时的安装过程

本文主要是介绍【android】应用程序安装过程分析(二)——————系统正常运行时的安装过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/19578947

前言

我们知道,在android手机上安装一个apk很简单,只要打开apk文件,默认就会弹出安装界面,然后点击确定,经过若干秒后,apk就安装成功了,可是你知道apk的安装过程是什么吗?你知道android系统在安装一个apk的时候都干了什么吗?在本文中,将一一解答这个问题。简单来说,apk的安装过程分两步:第一步,将apk文件复制到程序目录下(/data/app/);第二步,为应用创建数据目录(/data/data/package name/)、提取dex文件到指定目录(/data/dalvik-cache/)、修改系统包管理信息。注意,本文的分析基于Android 4.3源码。

apk的复制过程

apk的安装从PackageManager的installApk方法开始,由于PackageManager所对应的binder服务为PackageManagerService(PMS),所以,真正的安装过程都在PackageManagerService中完成。PackageManagerService的installApk方法最终调用了installPackageWithVerificationAndEncryption方法,该方法的核心就是在最后发送了一个INIT_COPY的消息,这个消息的含义是完成apk的拷贝过程。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void installPackageWithVerificationAndEncryption(Uri packageURI,  
  2.         IPackageInstallObserver observer, int flags, String installerPackageName,  
  3.         VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {  
  4.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,  
  5.             null);  
  6.   
  7.     final int uid = Binder.getCallingUid();  
  8.     if (isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) {  
  9.         try {  
  10.             observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);  
  11.         } catch (RemoteException re) {  
  12.         }  
  13.         return;  
  14.     }  
  15.   
  16.     UserHandle user;  
  17.     if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {  
  18.         user = UserHandle.ALL;  
  19.     } else {  
  20.         user = new UserHandle(UserHandle.getUserId(uid));  
  21.     }  
  22.   
  23.     final int filteredFlags;  
  24.   
  25.     if (uid == Process.SHELL_UID || uid == 0) {  
  26.         if (DEBUG_INSTALL) {  
  27.             Slog.v(TAG, "Install from ADB");  
  28.         }  
  29.         filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;  
  30.     } else {  
  31.         filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;  
  32.     }  
  33.   
  34.     verificationParams.setInstallerUid(uid);  
  35.   
  36.     final Message msg = mHandler.obtainMessage(INIT_COPY);  
  37.     msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,  
  38.             verificationParams, encryptionParams, user);  
  39.     mHandler.sendMessage(msg);  
  40. }  

通过分析代码可以发现,真正实现apk拷贝的方法是InstallParams的handleStartCopy方法,InstallParams中有重试机制,拷贝如果失败的话会重试,最多重试4次。在拷贝之前,还必须做一件事情,那就是绑定media container service,安装过程中一些状态的检查会用到这个服务,代码如下所示:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class PackageHandler extends Handler {  
  2.     private boolean mBound = false;  
  3.     final ArrayList<HandlerParams> mPendingInstalls =  
  4.         new ArrayList<HandlerParams>();  
  5.   
  6.     private boolean connectToService() {  
  7.         if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +  
  8.                 " DefaultContainerService");  
  9.         Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);  
  10.         Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);  
  11.         if (mContext.bindServiceAsUser(service, mDefContainerConn,  
  12.                 Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {  
  13.             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  14.             mBound = true;  
  15.             return true;  
  16.         }  
  17.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  18.         return false;  
  19.     }  
  20.     ...  
  21. }  

现在分析一下InstallParams的handleStartCopy方法,这个方法很长,代码就不帖出来了,大家可以自己去看看,这里主要分析下它的工作流程:

1. 检查安装位置标记位是否有冲突,如果有冲突,则安装失败,这里的有冲突是指“一个apk同时要求被安装到内部存储和sd卡”

2. 调用MCS服务的getMinimalPackageInfo方法来得到apk的推荐安装位置,并检查是否能够进行正常的安装。在这一步,有可能抛出一些无法安装的状态位:存储空间不足、程序已经安装、无效的apk文件等,这个时候安装过程终止

3. 到这一步,表示程序可以正常安装,同时MCS服务服务可能会根据需要调整安装位置,在InstallParams的installLocationPolicy中完成

4. 文件的复制过程,PMS针对内部存储和sd卡分别提供了一个类:FileInstallArgs和AsecInstallArgs,并分别调用二者的copyApk方法来完成apk的复制过程

经过了上面4步,待安装apk已经被复制到了/data/app/目录了。

apk的解析过程

上面,apk已经被复制到了/data/app/目录,安装的第一步已经完成,那么系统是什么时候对apk进行dex提取和解析的呢,这还要从PMS说起,在PMS内部有一个AppDirObserver类,顾名思义,它的作用是应用目录观察者,它时刻观察着应用目录/data/app/,当目录内部结构改变的时候(创建文件和删除文件)它会做出相应行为,下面看下它的代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private final class AppDirObserver extends FileObserver {  
  2.     public AppDirObserver(String path, int mask, boolean isrom) {  
  3.         super(path, mask);  
  4.         mRootDir = path;  
  5.         mIsRom = isrom;  
  6.     }  
  7.     //在/data/app/目录下添加或删除apk的时候,此方法会被调用  
  8.     public void onEvent(int event, String path) {  
  9.         String removedPackage = null;  
  10.         int removedAppId = -1;  
  11.         int[] removedUsers = null;  
  12.         String addedPackage = null;  
  13.         int addedAppId = -1;  
  14.         int[] addedUsers = null;  
  15.   
  16.         // TODO post a message to the handler to obtain serial ordering  
  17.         synchronized (mInstallLock) {  
  18.             String fullPathStr = null;  
  19.             File fullPath = null;  
  20.             if (path != null) {  
  21.                 fullPath = new File(mRootDir, path);  
  22.                 fullPathStr = fullPath.getPath();  
  23.             }  
  24.   
  25.             if (DEBUG_APP_DIR_OBSERVER)  
  26.                 Log.v(TAG, "File " + fullPathStr + " changed: " + Integer.toHexString(event));  
  27.   
  28.             if (!isPackageFilename(path)) {  
  29.                 if (DEBUG_APP_DIR_OBSERVER)  
  30.                     Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr);  
  31.                 return;  
  32.             }  
  33.   
  34.             // Ignore packages that are being installed or  
  35.             // have just been installed.  
  36.             if (ignoreCodePath(fullPathStr)) {  
  37.                 return;  
  38.             }  
  39.             PackageParser.Package p = null;  
  40.             PackageSetting ps = null;  
  41.             // reader  
  42.             synchronized (mPackages) {  
  43.                 p = mAppDirs.get(fullPathStr);  
  44.                 if (p != null) {  
  45.                     ps = mSettings.mPackages.get(p.applicationInfo.packageName);  
  46.                     if (ps != null) {  
  47.                         removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);  
  48.                     } else {  
  49.                         removedUsers = sUserManager.getUserIds();  
  50.                     }  
  51.                 }  
  52.                 addedUsers = sUserManager.getUserIds();  
  53.             }  
  54.             //当apk被删除的时候,往往意味着这个apk被卸载  
  55.             if ((event&REMOVE_EVENTS) != 0) {  
  56.                 if (ps != null) {  
  57.                     if (DEBUG_REMOVE) Slog.d(TAG, "Package disappeared: " + ps);  
  58.                     //removePackageLI方法完成卸载apk的主要功能  
  59.                     removePackageLI(ps, true);  
  60.                     removedPackage = ps.name;  
  61.                     removedAppId = ps.appId;  
  62.                 }  
  63.             }  
  64.               
  65.             //新添加了一个apk,往往意味着一个新的apk被安装  
  66.             if ((event&ADD_EVENTS) != 0) {  
  67.                 if (p == null) {  
  68.                     if (DEBUG_INSTALL) Slog.d(TAG, "New file appeared: " + fullPath);  
  69.                     //scanPackageLI方法完成了apk安装的第二个步骤  
  70.                     p = scanPackageLI(fullPath,  
  71.                             (mIsRom ? PackageParser.PARSE_IS_SYSTEM  
  72.                                     | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |  
  73.                             PackageParser.PARSE_CHATTY |  
  74.                             PackageParser.PARSE_MUST_BE_APK,  
  75.                             SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,  
  76.                             System.currentTimeMillis(), UserHandle.ALL);  
  77.                     if (p != null) {  
  78.                         /* 
  79.                          * TODO this seems dangerous as the package may have 
  80.                          * changed since we last acquired the mPackages 
  81.                          * lock. 
  82.                          */  
  83.                         // writer  
  84.                         synchronized (mPackages) {  
  85.                             updatePermissionsLPw(p.packageName, p,  
  86.                                     p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);  
  87.                         }  
  88.                         addedPackage = p.applicationInfo.packageName;  
  89.                         addedAppId = UserHandle.getAppId(p.applicationInfo.uid);  
  90.                     }  
  91.                 }  
  92.             }  
  93.   
  94.             // reader  
  95.             synchronized (mPackages) {  
  96.                 mSettings.writeLPr();  
  97.             }  
  98.         }  
  99.   
  100.         //下面两个if语句块大家应用不陌生吧,在我们的应用中想监听应用的安装和卸载,  
  101.         //就是通过收听ACTION_PACKAGE_ADDED和ACTION_PACKAGE_REMOVED这两个广播来实现的  
  102.         if (removedPackage != null) {  
  103.             Bundle extras = new Bundle(1);  
  104.             extras.putInt(Intent.EXTRA_UID, removedAppId);  
  105.             extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);  
  106.             sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,  
  107.                     extras, nullnull, removedUsers);  
  108.         }  
  109.         if (addedPackage != null) {  
  110.             Bundle extras = new Bundle(1);  
  111.             extras.putInt(Intent.EXTRA_UID, addedAppId);  
  112.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,  
  113.                     extras, nullnull, addedUsers);  
  114.         }  
  115.     }  
  116.   
  117.     private final String mRootDir;  
  118.     private final boolean mIsRom;  
  119. }  

下面,我们主要分析一下scanPackageLI方法,还是仅仅分析,不帖代码,因为代码太长了,帖出来没法看了,这个方法不仅仅是完成apk包的扫描,还解析AndroidManifest.xml文件并提取出所有的intent-filter和permission信息,apk安装的主要功能都由它来完成的,当apk包扫描完成后,系统会调用updatePermissionsLPw方法更新系统所具有的权限。

scanPackageLI方法有两个,其第一个参数分别接受File和PackageParser.Package类型,第一个方法会从File中提取出package信息然后再调用第二个方法,下面分析第二个scanPackageLI方法,其完成的事情如下:

1. 如果包名是android,则会做一些特殊处理,这个包名为android的应用是系统内部应用的,其他应用的包名如果叫android则安装会有问题,大家可以试一下

2. 解析常见的use-feature、shared-userId、use-library标签并保存到成员变量中

3. 进行签名验证,对应的方法是verifySignaturesLP,验证失败则应用无法安装

4. 创建应用程序目录/data/data/包名,同时将apk中提取出dex文件并保存到/data/dalvik-cache,把apk当做zip解压就能得到dex文件

5. 解析AndroidManifest.xml文件,提取出所需信息,包括具有intent-filter的四大组件信息(Activity、Service、BroadcastReceiver、ContentProvider)和声明的系统权限等

到此为止,scanPackageLI方法结束了。而updatePermissionsLPw的作用是对系统中所有的权限进行更新,大家可以查看下/system/etc/permissons目录,下面定义了android系统中所有的权限,开发中最常用的权限定义在目录下的platform.xml里面,大家可以打开看看,可以看到常见的访问网络、读写外部存储等权限等都是在这里定义的。权限更新完毕以后,系统就会发送ACTION_PACKAGE_ADDED广播,告知所有应用有新应用安装了。另外,大家可以查看下data/system/目录,里面有两个文件packages.list和packages.xml,在packages.list里面放的是手机上安装的所有应用列表,而packages.xml中存放的是所有应用的设置应用,比如一个应用声明了哪些系统权限就定义在这里面。关于应用的卸载,我们可以想到是应用安装过程的逆过程,大致要做的是:停止应用、删除各种文件,更新系统设置、权限等,大家感兴趣自己看一下,完全是安装过程的逆过程,这里不介绍了。

这篇关于【android】应用程序安装过程分析(二)——————系统正常运行时的安装过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1046518

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

CentOS7安装配置mysql5.7 tar免安装版

一、CentOS7.4系统自带mariadb # 查看系统自带的Mariadb[root@localhost~]# rpm -qa|grep mariadbmariadb-libs-5.5.44-2.el7.centos.x86_64# 卸载系统自带的Mariadb[root@localhost ~]# rpm -e --nodeps mariadb-libs-5.5.44-2.el7

Centos7安装Mongodb4

1、下载源码包 curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.1.tgz 2、解压 放到 /usr/local/ 目录下 tar -zxvf mongodb-linux-x86_64-rhel70-4.2.1.tgzmv mongodb-linux-x86_64-rhel70-4.2.1/

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p