原创内容,转载请注明出处,多谢配合。

一、APK组成

在APK的安装流程,在此之前先简单了解下APK组成:

目录/文件

描述

assert

存放的原生资源文件,通过AssetManager类访问。

lib

存放库文件。

META-INF

保存应用的签名信息,签名信息可以验证APK文件的完整性。

res

存放资源文件。res中除了raw子目录,其他的子目录都参与编译,这些子目录下的资源是通过编译出的R类在代码中访问。

AndroidManifest.xml

用来声明应用程序的包名称、版本、组件和权限等数据。 apk中的AndroidManifest.xml经过压缩,可以通过AXMLPrinter2工具解开。

classes.dex

Java源码编译后生成的Java字节码文件。

resources.arsc

编译后的二进制资源文件。

二、Apk安装方法

APK的安装场景主要有以下几种:

通过adb命令安装:adb 命令包括adb push/install

用户下载的Apk,通过系统安装器packageinstaller安装该Apk。packageinstaller是系统内置的应用程序,用于安装和卸载应用程序。

系统开机时安装系统应用。

电脑或者手机上的应用商店自动安装。

这里主要分析下PackageInstaller安装Apk流程:

按如上流程,挑比较重要的流程分析下:

packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java

private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {

final PackageInstaller packageInstaller = pm.getPackageInstaller();

PackageInstaller.Session session = null;

try {

final String packageLocation = mPackageURI.getPath();

final File file = new File(packageLocation);

final int sessionId = packageInstaller.createSession(params);

final byte[] buffer = new byte[65536];

session = packageInstaller.openSession(sessionId);

final InputStream in = new FileInputStream(file);

final long sizeBytes = file.length();

final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);

try {

int c;

while ((c = in.read(buffer)) != -1) {

out.write(buffer, 0, c);

if (sizeBytes > 0) {

final float fraction = ((float) c / (float) sizeBytes);

session.addProgress(fraction);

}

}

session.fsync(out);

} finally {

IoUtils.closeQuietly(in);

IoUtils.closeQuietly(out);

}

// Create a PendingIntent and use it to generate the IntentSender

Intent broadcastIntent = new Intent(BROADCAST_ACTION + mInstallId);

PendingIntent pendingIntent = PendingIntent.getBroadcast(

InstallAppProgress.this /*context*/,

sessionId,

broadcastIntent,

PendingIntent.FLAG_UPDATE_CURRENT);

session.commit(pendingIntent.getIntentSender());

...

}

将APK的信息通过IO流的形式写入到PackageInstaller.Session中。调用PackageInstaller.Session的commit方法,将APK的信息交由PMS处理。

到了PMS这边有如下的关键几步:

1) PackageHandler处理安装消息

PMS中doHandleMessage方法用于处理各个类型的消息:

void doHandleMessage(Message msg) {

switch (msg.what) {

case INIT_COPY: {

// 将新的请求加入到mPendingIntalls中,等待MCS_BOUND阶段处理

break;

}

case MCS_BOUND: {

//如果mPendingInstalls不为空,调用InstallParams.startCopy函数处理安装请求

满足条件触发startCopy()

break;

}

}

}

这里会起一个新的进程DefaultContainerService用于检查和复制可移动文件的服务。它运行在com.android.defcontainer进程,通过IMediaContainerService和PMS进行IPC通信。

final boolean startCopy() {

boolean res;

try {

if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

//尝试安装4次,超过次数就放弃这个安装请求

if (++mRetries > MAX_RETRIES) {

Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");

mHandler.sendEmptyMessage(MCS_GIVE_UP);

handleServiceError();

return false;

} else {

handleStartCopy(); //复制APK

res = true;

}

} catch (RemoteException e) {

if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");

mHandler.sendEmptyMessage(MCS_RECONNECT);

res = false;

}

handleReturnCode(); //安装APK

return res;

}

2) 复制APK

handleStartCopy()主要功能是获取安装位置信息以及复制apk到指定位置。抽象类InstallArgs中的copyApk负责复制APK文件,具体实现在子类FileInstallArgs和SdInstallArgs里面。最终通过IMediaContainerService跨进程调用DefaultContainerService的copyPackage方法,在DefaultContainerService所在的进程中将APK复制到临时存储目录,比如/data/app/vmdl18300388.tmp/base.apk。

3) 安装APK

private void processPendingInstall(final InstallArgs args, final int currentStatus) {

// Queue up an async operation since the package installation may take a little while.

mHandler.post(new Runnable() {

public void run() {

mHandler.removeCallbacks(this);

// Result object to be returned

PackageInstalledInfo res = new PackageInstalledInfo();

res.returnCode = currentStatus;

res.uid = -1;

res.pkg = null;

res.removedInfo = new PackageRemovedInfo();

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {

//预安装

args.doPreInstall(res.returnCode);

synchronized (mInstallLock) {

//安装

installPackageLI(args, res);

}

//结束安装

args.doPostInstall(res.returnCode, res.uid);

}

if (!doRestore) {

//如果完成安装的msg,package add的广播将在此处发送

Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);

mHandler.sendMessage(msg);

}

}

});

}

安装的核心方法是:installPackageLI

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

PackageParser pp = new PackageParser();

...

// 解析我们的package

pkg = pp.parsePackage(tmpPackageFile, parseFlags);

//

mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,

null /* instructionSets */, false /* checkProfiles */,

getCompilerFilterForReason(REASON_INSTALL),

getOrCreateCompilerPackageStats(pkg),

mDexManager.isUsedByOtherApps(pkg.packageName));

...

//安装package

if (replace) {

// 更新已经存在的packages

replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,

installerPackageName, res);

} else {

// 安装新的packages

installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,

args.user, installerPackageName, volumeUuid, res);

}

}

...

}

先看PackageParser的parsePackage:

public Package parsePackage(File packageFile, int flags, boolean useCaches)

throws PackageParserException {

...

if (packageFile.isDirectory()) {

//多apk,内部会通过parseApkLite方法解析每个Mutiple APK,得到每个Mutiple APK对应的ApkLite轻量级APK信息

parsed = parseClusterPackage(packageFile, flags);

} else {

//单apk

parsed = parseMonolithicPackage(packageFile, flags);

}

...

return parsed;

}

注:

Android5.0引入了Split APK机制,这是为了解决65536上限以及APK安装包越来越大等问题。Split APK机制可以将一个APK,拆分成多个独立APK。

在引入了Split APK机制后,APK有两种分类:

Single APK:安装文件为一个完整的APK,即base APK。Android称其为Monolithic。

Mutiple APK:安装文件在一个文件目录中,其内部有多个被拆分的APK,这些APK由一个 base APK和一个或多个split APK组成。Android称其为Cluster。

之后调用parseBaseApk 以及 parseBaseApkCommon,主要的xml解析在parseBaseApkCommon:主要用来解>析APK的AndroidManifest中的各个标签,比如application、permission、uses-sdk、feature-group等等。

PackageParser主要负责解析AndroidManifest

总结下installPackageLI过程:

PackageParser$parsePackage,主要是解析APK的AndroidManifest.xml,将每个标签对应的信息添加到Package的相关列表中,如将下的信息添加到Package的activities列表等。

加载apk证书,获取签名信息。

检查目前安装的APK是否在系统中已存在:

已存在,则调用replacePackageLIF进行替换安装。

不存在,否则调用installNewPackageLIF进行安装。

后面的replacePackageLIF和installNewPackageLIF最终都会走scanPackageTracedLI。

参考:

Android 安装apk流程,Android PMS(二)-Apk安装流程相关推荐

  1. mysql 数据库第二次安装不了_mysql数据库二次安装无法启动

    Starting MySQL...... ERROR! Manager of pid-file quit without updating file. 可选拍错思路: my.cnf 配置文件参数错误. ...

  2. Android Adb命令(1) - 查看所有的Apk包 / 安装install 和卸载uninstall

    一.查找 1.打印本机所有软件包 adb shell pm list packages 2.输出和安装包相关联的文件(包括apk的路径) adb shell pm list packages -f 3 ...

  3. 安装和配置 Android Studio

    如觉内容冗长,请在页面内再次搜索关键词,定位所需内容. 一.配置好JDK,即java环境. 1.JDK下载 可以直接使用安装Android Studio后内置的jre.然而内置的jre版本较高(1.1 ...

  4. 关于MySQL的二次安装问题

    由于各种原因需要重新下载并安装mysql,mysql的二次安装也出现了各种问题.下面将这些出现在问题作一次说明并给了一些解决问题的方案可供参考. 1.首先要把已经安装的mysql删除干净,这可以说是一 ...

  5. 2023最新最全git安装教程,保姆级手把手式安装!!!

            目录 一.git简介 二.安装过程 1.首先进入git的官网:https://git-scm.com/然后选择Downloads. 2.接着选择与自己电脑系统对应的下载选项,我的电脑是 ...

  6. 【OWA】03安装部署:OWA(Office Web Apps)安装和部署

    前言 在上一篇咱们把owa服务器加入到了域控中,可参考[OWA]02加入域控:将owa服务器加入域控(把计算机加入到域中), 接下来就在这台服务器上安装和配置OWA相关服务 [OWA]01环境准备:通 ...

  7. 虚拟机Linux6下安装Oracle 11G RAC(一)安装准备

    虚拟机Linux6下安装Oracle 11G RAC(二)安装集群和DBMS 虚拟机Linux6下安装Oracle 11G RAC(三)创建数据磁盘组和实例 最近总在linux6的虚拟机上装Oracl ...

  8. Android包管理机制(三)PMS处理APK的安装

    本文首发于微信公众号「刘望舒」 关联系列 Android包管理机制系列 前言 在上一篇文章Android包管理机制(二)PackageInstaller安装APK中,我们学习了PackageInsta ...

  9. 【Android 逆向】逆向修改游戏应用 ( APK 解析工具 | 解包 -> 分析 -> 重打包 -> 签名 流程 )

    文章目录 一.APK 解析工具 二.解包 -> 分析 -> 重打包 -> 签名 流程 一.APK 解析工具 使用 [Android 逆向]使用 Python 编写 APK 批处理分析 ...

最新文章

  1. python -- plt图表
  2. android点击事件注册方式,Android界面控件(3)— 注册同一个点击事件的监听器...
  3. 如何远程链接linux服务器,如何远程登陆Linux/Windows服务器
  4. IT应聘者的简历应该是怎么样的?
  5. 小猿圈web前端之网站性能优化方案
  6. ajax前端post请求json格式参数,前端js导出,参数以json格式发送post请求
  7. linux下samba环境搭建
  8. 搞了很久终于突破了一点瓶颈,fighting!!!!!!!!!!!!!!!!
  9. U3D连接Steamworks SDK制作成就系统
  10. 实现不同电脑共享一套鼠标键盘------ShareMouse
  11. 网络协议学习--SOAP协议(二)
  12. java bss_数据段、代码段、堆栈段、BSS段的区别
  13. 贷款综合管理系统搭建-全流程智能、高效、安全管理解决方案
  14. android 11.0 状态栏通知栏的图标为白色解决方案
  15. 那些令人发燥的JAVA虚引用
  16. 无为WiFi正在被DD
  17. 基于javaweb+jsp的设备维修管理系统(JavaWeb MySQL JSP Bootstrap Servlet SSM SpringBoot)
  18. Github官网无法访问问题
  19. 里恩临床试验电子数据采集系统(EDC)介绍
  20. python发微信语音没声音怎么回事_苹果手机微信语音没声音怎么回事?

热门文章

  1. QT生成动态链接库及调用详细步骤
  2. 求解大规模稀疏多目标优化问题的一种进化算法(SparseEA)笔记
  3. android 轮换实现动画,怎么在Android中实现一个首页无限轮播功能
  4. 素雅的登录界面,简单而优雅
  5. 19款奥迪a5支持Android,19款奥迪a5有敝篷版吗?
  6. 基于Linux用C语言实现TCP半双工通信和UDP半双工通信
  7. C语言求两个正整数的最小公倍数
  8. 关于STM32G071RB的ADC采集误差问题
  9. CMD(windows)操作命令大全
  10. 平台业务收款分账产品设计 - 支付计费分账