Appearance
Android 包安装流程深度解析
目录
- 包安装概述
- 安装方式详解
- PackageInstallerService
- [PMS 解析 APK](#4-pms 解析 apk)
- 签名验证机制
- 权限授予流程
- [dex2oat 优化](#7-dex2oat 优化)
- 安装失败原因
- 增量更新
- 面试考点
1. 包安装概述
1.1 APK 包结构
APK 文件结构详解:
┌─────────────────────────────────────────────────────────────┐
│ │
│ APK 文件结构 │
│ ├─ META-INF/ │
│ │ ├─ MANIFEST.MF # 清单文件 │
│ │ ├─ SIGNATURE.RSA # 签名文件 │
│ │ └─ CODECSIGN # 签名校验 │
│ ├─ res/ # 资源目录 │
│ │ ├─ drawable/ # 图片资源 │
│ │ ├─ layout/ # 布局文件 │
│ │ ├─ values/ # 值资源 │
│ │ └─ ... # 其他资源 │
│ ├─ assets/ # 原始资产文件 │
│ ├─ classes.dex # 编译后的 DEX 文件 │
│ ├─ resources.arsc # 资源映射文件 │
│ ├─ AndroidManifest.xml # 清单文件 │
│ ├─ lib/ # 原生库 │
│ │ ├─ arm64-v8a/ # ARM64 架构 │
│ │ ├─ armeabi-v7a/ # ARMv7 架构 │
│ │ ├─ x86/ # x86 架构 │
│ │ └─ x86_64/ # x86_64 架构 │
│ └─ ... # 其他文件 │
│ │
└─────────────────────────────────────────────────────────────┘1.2 安装流程总览
Android 包安装完整流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 用户触发安装 │
│ ├─ 点击安装包 │
│ ├─ ADB 安装 │
│ └─ 系统更新 │
│ │
│ 2. PackageInstallerService │
│ ├─ 接收安装请求 │
│ ├─ 验证安装权限 │
│ └─ 调用 PMS 安装 │
│ │
│ 3. PackageManagerService 处理 │
│ ├─ 解析 APK │
│ ├─ 验证签名 │
│ ├─ 分配 UID/GID │
│ ├─ 复制文件 │
│ ├─ 编译 DEX │
│ └─ 注册组件 │
│ │
│ 4. dex2oat 编译优化 │
│ ├─ DEX 到 OAT 编译 │
│ ├─ 预优化 │
│ └─ 缓存 OAT 文件 │
│ │
│ 5. 安装完成 │
│ ├─ 更新包信息 │
│ ├─ 通知安装完成 │
│ └─ 清理临时文件 │
│ │
└─────────────────────────────────────────────────────────────┘2. 安装方式详解
2.1 系统应用安装
系统应用在系统启动时由 PMS 扫描安装。
系统应用安装流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ System Image 分区 │
│ ├─ /system/app/ # 系统应用 │
│ ├─ /system/priv-app/ # 特权应用 │
│ ├─ /system/product/ # 产品分区应用 │
│ └─ /system/vendor/ # 厂商分区应用 │
│ │
│ SystemServer 启动时 │
│ ├─ PMS 扫描系统应用目录 │
│ ├─ 解析 APK 文件 │
│ ├─ 验证签名 │
│ ├─ 安装到系统分区 │
│ └─ 注册系统组件 │
│ │
└─────────────────────────────────────────────────────────────┘系统应用特点:
- 无需用户授权即可安装
- 拥有系统权限
- 无法卸载(某些情况下可以禁用)
- 位于系统分区
2.2 用户应用安装
用户应用安装流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 用户点击安装包 │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PackageInstaller 显示安装界面 │ │
│ │ - 应用名称 │ │
│ │ - 应用图标 │ │
│ │ - 权限列表 │ │
│ │ - 安装按钮 │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ 用户点击"安装" │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PackageInstallerService 接收请求 │ │
│ │ - 验证安装权限 │ │
│ │ - 调用 PMS.installPackageAsUser() │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ PackageManagerService 处理 │
│ ↓ │
│ 安装完成 │
│ │
└─────────────────────────────────────────────────────────────┘2.3 ADB 安装
ADB 安装命令:
bash
# 安装 APK 到默认用户
adb install app.apk
# 安装 APK 到指定用户
adb install --user 0 app.apk
# 允许降级安装
adb install -d app.apk
# 不替换现有应用
adb install -r app.apk
# 跳过权限验证
adb install --bypass-low-target-sdk-block app.apk
# 指定安装位置
adb install --pkgs-dir /data/app app.apkADB 安装源码分析:
java
// frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
public final int installPackageAsUser(String hostPath,
PackageInstallParams params,
int userId) {
// 1. 检查权限
enforceBlockInstallation();
// 2. 复制 APK 文件
File stagedFile = getPackageStagedFile(hostPath, userId);
// 3. 解析 APK
PackageParser.Package parsedPackage = new PackageParser();
parsedPackage.parse(stagedFile);
// 4. 验证签名
verifySignature(parsedPackage);
// 5. 安装应用
installPackage(parsedPackage, userId);
return 0;
}2.4 安装权限对比
安装权限对比表:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 安装方式 权限要求 安装位置 卸载权限 │
│ ───────────────────────────────────────────────────── │
│ 系统应用 无 /system/app 需 Root │
│ 用户应用 无 /data/app 可卸载 │
│ ADB 安装 ADB 权限 /data/app 可卸载 │
│ 商店安装 无 /data/app 可卸载 │
│ 特权应用 系统签名 /system/priv 需系统权限 │
│ │
└─────────────────────────────────────────────────────────────┘3. PackageInstallerService
3.1 PackageInstallerService 概述
PackageInstallerService 负责处理应用安装的前端交互,是用户和 PMS 之间的桥梁。
PackageInstallerService 架构:
┌─────────────────────────────────────────────────────────────┐
│ │
│ PackageInstallerService │
│ ├─ PackageInstaller │
│ │ ├─ 显示安装界面 │
│ │ ├─ 接收用户操作 │
│ │ └─ 调用 InstallerService │
│ │ │
│ ├─ InstallerService │
│ │ ├─ 处理安装请求 │
│ │ ├─ 验证权限 │
│ │ └─ 调用 PMS 安装 │
│ │ │
│ └─ PackageInstallerActivity │
│ ├─ 显示应用信息 │
│ ├─ 显示权限列表 │
│ └─ 处理用户确认 │
│ │
└─────────────────────────────────────────────────────────────┘3.2 PackageInstaller 源码分析
PackageInstaller.java:
java
// frameworks/base/core/java/com/android/content/pm/IPackageInstaller.java
public abstract class PackageInstaller {
// 安装会话状态
public static final int SESSION_INSTALL_PENDING = 0;
public static final int SESSION_INSTALL_RUNNING = 1;
public static final int SESSION_INSTALL_SUCCESS = 2;
public static final int SESSION_INSTALL_FAILURE = 3;
// 安装会话
public abstract class Session {
// 安装应用
public abstract void install() throws RemoteException;
// 获取安装状态
public abstract int getStatus() throws RemoteException;
// 获取安装进度
public abstract int getProgress() throws RemoteException;
// 验证 APK
public abstract void verify() throws RemoteException;
}
// 创建安装会话
public abstract int createSession(Params params) throws RemoteException;
// 获取会话
public abstract Session getSession(int sessionId) throws RemoteException;
}PackageInstallerService.java:
java
// frameworks/base/services/java/com/android/server/pm/PackageInstallerService.java
public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public int createSession(Params params) {
// 1. 验证参数
validateParams(params);
// 2. 创建会话
Session session = new Session(params);
mSessions.put(session.getId(), session);
// 3. 返回会话 ID
return session.getId();
}
@Override
public Session getSession(int sessionId) {
// 1. 获取会话
return mSessions.get(sessionId);
}
}
// Session 类
private static class Session {
private final int mId;
private final Params mParams;
private int mStatus = SESSION_INSTALL_PENDING;
private int mProgress = 0;
public Session(Params params) {
mId = generateId();
mParams = params;
}
@Override
public void install() {
// 1. 验证会话
checkSession();
// 2. 调用 PMS 安装
IPackageManager pm = PackageManager.getService();
pm.installPackage(mParams.packagePath, mParams.installFlags);
// 3. 更新状态
mStatus = SESSION_INSTALL_RUNNING;
}
}3.3 安装会话管理
安装会话状态流转:
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────┐ 创建会话 ┌─────────────┐ │
│ │ None │ ───────→ │ PENDING │ │
│ └─────────────┘ └─────────────┘ │
│ ↓ │
│ 用户点击安装 │
│ ↓ │
│ ┌─────────────┐ 安装失败 ┌─────────────┐ │
│ │ CANCELLED │ ←──────── │ RUNNING │ │
│ └─────────────┘ 取消 └─────────────┘ │
│ ↓ │
│ 安装完成 │
│ ↓ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ SUCCESS │ 成功 │ FAILURE │ │
│ └─────────────┘ 状态 └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘4. PMS 解析 APK
4.1 PackageParser 概述
PackageParser 负责解析 APK 文件,提取应用信息。
PackageParser 解析流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ PackageParser │
│ ├─ 解析 AndroidManifest.xml │
│ │ ├─ 应用包名 │
│ │ ├─ 应用版本 │
│ │ ├─ 应用组件 │
│ │ ├─ 应用权限 │
│ │ └─ 应用特征 │
│ │ │
│ ├─ 解析 resources.arsc │
│ │ ├─ 资源 ID 映射 │
│ │ ├─ 资源类型 │
│ │ └─ 资源字符串 │
│ │ │
│ ├─ 解析 classes.dex │
│ │ ├─ 类列表 │
│ │ ├─ 方法列表 │
│ │ └─ 字段列表 │
│ │ │
│ └─ 解析其他文件 │
│ ├─ lib 库文件 │
│ ├─ assets 资源 │
│ └─ 其他文件 │
│ │
└─────────────────────────────────────────────────────────────┘4.2 AndroidManifest.xml 解析
AndroidManifest.xml 结构:
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto">
<!-- 权限声明 -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<!-- 特性声明 -->
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<!-- 应用组件 -->
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<!-- Activity -->
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Service -->
<service android:name=".MyService"
android:exported="false"/>
<!-- BroadcastReceiver -->
<receiver android:name=".MyReceiver"
android:exported="true"/>
<!-- ContentProvider -->
<provider android:name=".MyProvider"
android:authorities="com.example.app.provider"
android:exported="true"/>
</application>
</manifest>PackageParser 解析源码:
java
// frameworks/base/packages/PackageManagerService/PackageParser.java
public class PackageParser {
public Package parsePackage(File packageFile) throws ParseException {
// 1. 打开 APK 文件
ZipFile zipFile = new ZipFile(packageFile);
// 2. 解析 AndroidManifest.xml
XmlResourceParser parser = openXml(zipFile, "AndroidManifest.xml");
Package parsedPackage = parseManifest(parser);
// 3. 解析 resources.arsc
Resources resources = loadResources(zipFile);
// 4. 解析其他文件
parseAssets(zipFile);
return parsedPackage;
}
private Package parseManifest(XmlResourceParser parser) {
Package pkg = new Package();
try {
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.getName().equals("manifest")) {
// 解析包名
pkg.packageName = parser.getAttributeValue(null, "package");
// 解析版本
pkg.versionCode = parser.getIntAttribute(null, "versionCode");
pkg.versionName = parser.getAttributeValue(null, "versionName");
// 解析权限
pkg.permissions = parsePermissions(parser);
// 解析组件
pkg.activities = parseComponents(parser, "activity");
pkg.services = parseComponents(parser, "service");
pkg.receivers = parseComponents(parser, "receiver");
pkg.providers = parseComponents(parser, "provider");
}
}
} catch (Exception e) {
throw new ParseException("Failed to parse manifest");
}
return pkg;
}
}4.3 Package 对象结构
Package 对象:
java
// frameworks/base/packages/PackageManagerService/PackageParser.java
public class Package {
// 包名
public final String packageName;
// 版本信息
public final int versionCode;
public final String versionName;
// 应用信息
public final ApplicationInfo applicationInfo;
// 组件列表
public final ArrayList<Activity> activities;
public final ArrayList<Service> services;
public final ArrayList<Receiver> receivers;
public final ArrayList<Provider> providers;
// 权限信息
public final ArrayList<Permission> permissions;
public final String[] requestedPermissions;
// 签名信息
public final PackageSigner signer;
// 安装路径
public final String codePath;
public final String nativeLibPath;
// 其他信息
public final int flags;
public final int sdkVersion;
public final String targetSdkVersion;
}5. 签名验证机制
5.1 签名概述
签名是 Android 应用安全的基础,用于验证应用来源和完整性。
Android 签名机制:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 签名流程 │
│ ├─ 开发者生成密钥对 │
│ │ ├─ 公钥 │
│ │ └─ 私钥 │
│ │ │
│ ├─ 使用私钥对 APK 签名 │
│ │ ├─ 计算 APK 摘要 │
│ │ ├─ 使用私钥加密摘要 │
│ │ └─ 生成签名文件 │
│ │ │
│ └─ 安装时验证签名 │
│ ├─ 使用公钥解密签名 │
│ ├─ 计算 APK 摘要 │
│ └─ 对比摘要验证完整性 │
│ │
└─────────────────────────────────────────────────────────────┘5.2 签名生成
使用 keytool 生成密钥:
bash
# 生成密钥库
keytool -genkey -v -keystore my-release-key.jks \
-alias my-key-alias \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# 查看密钥库信息
keytool -list -v -keystore my-release-key.jks使用 jarsigner 签名:
bash
# 签名 APK
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 \
-keystore my-release-key.jks \
release-unsigned.apk \
my-key-alias5.3 签名验证源码
PackageSigner 验证:
java
// frameworks/base/packages/PackageManagerService/PackageSigner.java
public class PackageSigner {
private final Certificate[] mCertificates;
private final SigningDetails mSigningDetails;
public PackageSigner(Certificate[] certificates) {
mCertificates = certificates;
mSigningDetails = new SigningDetails();
}
// 验证签名
public boolean verify(File packageFile) throws IOException {
// 1. 读取 APK 文件
JarFile jarFile = new JarFile(packageFile);
// 2. 获取签名信息
java.security.cert.Certificate[] certs = jarFile.getCertificates();
// 3. 验证签名
for (Certificate cert : certs) {
if (!verifySignature(cert)) {
return false;
}
}
return true;
}
private boolean verifySignature(Certificate cert) {
// 1. 获取公钥
PublicKey publicKey = cert.getPublicKey();
// 2. 验证签名
try {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
// 3. 计算 APK 摘要
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(readFile(packageFile));
byte[] digestBytes = digest.digest();
// 4. 验证签名
signature.update(digestBytes);
return signature.verify(getSignatureBytes());
} catch (Exception e) {
return false;
}
}
}5.4 签名方案演进
Android 签名方案演进:
┌─────────────────────────────────────────────────────────────┐
│ │
│ v1 签名 (Android 4.0+) │
│ ├─ 在 META-INF 目录中存储签名 │
│ ├─ 基于 JAR 签名机制 │
│ └─ 签名文件:CERT.SF, CERT.RSA │
│ │
│ v2 签名 (Android 7.0+) │
│ ├─ 全 APK 签名 │
│ ├─ 签名包含在 APK 头部 │
│ ├─ 签名验证更快 │
│ └─ 支持增量更新 │
│ │
│ v3 签名 (Android 8.0+) │
│ ├─ 在 v2 基础上增加证书吊销列表 │
│ ├─ 支持证书吊销 │
│ └─ 向后兼容 v2 │
│ │
│ v4 签名 (Android 9.0+) │
│ ├─ 在 v3 基础上增加包完整性验证 │
│ ├─ 支持 APK 完整性验证 │
│ └─ 向后兼容 v3 │
│ │
└─────────────────────────────────────────────────────────────┘6. 权限授予流程
6.1 权限分类
权限分类详解:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 普通权限 (Normal) │
│ ├─ 不影响用户隐私 │
│ ├─ 安装时自动授予 │
│ └─ 示例:INTERNET, ACCESS_NETWORK_STATE │
│ │
│ 危险权限 (Dangerous) │
│ ├─ 涉及用户隐私 │
│ ├─ 运行时请求 │
│ └─ 示例:CAMERA, LOCATION, CONTACTS │
│ │
│ 签名权限 (Signature) │
│ ├─ 需要系统签名 │
│ ├─ 仅限系统应用 │
│ └─ 示例:REBOOT, SET_DEBUG_APP │
│ │
│ 特殊权限 (Special) │
│ ├─ 需要用户手动授权 │
│ ├─ 通过系统设置授予 │
│ └─ 示例:BIND_ACCESSIBILITY_SERVICE, BIND_VPN_SERVICE │
│ │
└─────────────────────────────────────────────────────────────┘6.2 权限授予流程
运行时权限请求流程:
运行时权限请求:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 应用请求权限 │
│ ↓ │
│ Activity.requestPermissions() │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ AMS 处理权限请求 │ │
│ │ - 检查权限级别 │ │
│ │ - 检查是否已授权 │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ 系统显示权限对话框 │
│ ↓ │
│ 用户选择允许或拒绝 │
│ ↓ │
│ 更新权限数据库 │
│ ↓ │
│ 回调 onRequestPermissionsResult() │
│ │
└─────────────────────────────────────────────────────────────┘6.3 权限授予源码
PackageManagerService 权限授予:
java
// frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
public final void grantRuntimePermission(String packageName,
String permissionName,
int userId) {
// 1. 获取权限信息
PermissionInfo permissionInfo = getPermissionInfo(permissionName);
// 2. 验证权限
validatePermission(permissionInfo);
// 3. 更新权限授予状态
mPermissionEntries.put(packageName, permissionName, true);
// 4. 通知 AMS
mActivityManagerService.permissionGranted(packageName, permissionName);
}
// 权限检查
public final boolean checkPermission(String permissionName,
String packageName,
int userId) {
// 1. 获取权限授予状态
boolean granted = mPermissionEntries.get(packageName, permissionName);
// 2. 返回权限状态
return granted;
}7. dex2oat 优化
7.1 dex2oat 概述
dex2oat 是将 DEX 文件编译成 OAT 文件的工具,提升应用启动和运行性能。
dex2oat 编译流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ DEX 文件 │
│ ↓ │
│ dex2oat 编译器 │
│ ↓ │
│ OAT 文件 (Native 代码) │
│ ↓ │
│ 缓存到 /data/dalvik-vm-cache │
│ ↓ │
│ 应用启动时加载 OAT │
│ ↓ │
│ 更快的执行速度 │
│ │
└─────────────────────────────────────────────────────────────┘7.2 OAT 文件结构
OAT 文件结构:
┌─────────────────────────────────────────────────────────────┐
│ │
│ OAT 文件 │
│ ├─ OAT Header │
│ │ ├─ Magic Number │
│ │ ├─ Version │
│ │ └─ File Info │
│ │ │
│ ├─ Code Cache │
│ │ ├─ Compiled Methods │
│ │ ├─ JIT Code │
│ │ └─ Debug Info │
│ │ │
│ ├─ Image Segment │
│ │ ├─ Runtime Image │
│ │ ├─ Class Images │
│ │ └─ Static Data │
│ │ │
│ └─ DEX Files │
│ ├─ Primary DEX │
│ ├─ Secondary DEX │
│ └─ Optimization Info │
│ │
└─────────────────────────────────────────────────────────────┘7.3 编译模式
编译模式对比:
编译模式对比:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Speed Size Space │
│ ────────────────────────────────────────────────────── │
│ 快速启动 小文件 节省空间 │
│ 平衡模式 中等文件 平衡 │
│ 优化模式 大文件 占用更多空间 │
│ │
└─────────────────────────────────────────────────────────────┘8. 安装失败原因
8.1 常见安装失败原因
安装失败原因分类:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 签名相关 │
│ ├─ 签名验证失败 │
│ ├─ 签名不匹配 │
│ └─ 证书过期 │
│ │
│ 版本相关 │
│ ├─ 版本冲突 │
│ ├─ 无法降级安装 │
│ └─ 版本不兼容 │
│ │
│ 存储相关 │
│ ├─ 存储空间不足 │
│ ├─ 存储路径无效 │
│ └─ 存储权限不足 │
│ │
│ 权限相关 │
│ ├─ 权限拒绝 │
│ ├─ 权限不匹配 │
│ └─ 特殊权限未授权 │
│ │
│ 系统相关 │
│ ├─ 系统版本不兼容 │
│ ├─ 系统组件冲突 │
│ └─ 系统资源不足 │
│ │
└─────────────────────────────────────────────────────────────┘8.2 错误代码
PackageManager 安装错误代码:
java
// frameworks/base/core/java/com/android/content/pm/IPackageManager.java
public class PackageManager {
// 安装成功
public static final int INSTALL_SUCCEEDED = 1;
// 安装失败
public static final int INSTALL_FAILED_ALREADY_EXISTS = 2;
public static final int INSTALL_FAILED_INVALID_URI = 3;
public static final int INSTALL_FAILED_PARSE = 4;
public static final int INSTALL_FAILED_NEWER = 5;
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = 6;
public static final int INSTALL_FAILED_ARCH_MISMATCH = 7;
public static final int INSTALL_FAILED_NO_MATCHING_ABIS = 8;
public static final int INSTALL_FAILED_CONTAINER_DIR = 9;
public static final int INSTALL_FAILED_INTERNAL_ERROR = 10;
public static final int INSTALL_FAILED_INVALID_APK = 11;
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = 12;
public static final int INSTALL_FAILED_BLOCKED = 13;
public static final int INSTALL_FAILED_CANCELLED = 14;
public static final int INSTALL_FAILED_OLDER = 15;
public static final int INSTALL_FAILED_DOWNGRADE = 16;
public static final int INSTALL_FAILED_UPDATE_DATA_LOSS = 17;
public static final int INSTALL_FAILED_MISSING_FEATURE = 18;
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = 19;
public static final int INSTALL_FAILED_TEST_ONLY = 20;
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = 21;
public static final int INSTALL_FAILED_DEPENDENCY = 22;
}8.3 安装失败处理
异常处理代码:
java
// 安装失败处理
public void onInstallFailed(int errorCode, String message) {
switch (errorCode) {
case INSTALL_SUCCEEDED:
// 安装成功
break;
case INSTALL_FAILED_ALREADY_EXISTS:
// 已存在,提示用户更新
showUpdateDialog();
break;
case INSTALL_FAILED_INSUFFICIENT_STORAGE:
// 存储空间不足
showStorageError();
break;
case INSTALL_FAILED_INVALID_APK:
// APK 无效
showInvalidApkError();
break;
case INSTALL_FAILED_UPDATE_INCOMPATIBLE:
// 更新不兼容
showIncompatibleError();
break;
default:
// 其他错误
showError(errorCode, message);
}
}9. 增量更新
9.1 增量更新原理
增量更新只下载变更部分,减少下载时间和流量消耗。
增量更新流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 旧版本 APK 新版本 APK │
│ ↓ ↓ │
│ ┌──────────────────────────────────────┐ │
│ │ 对比分析差异 │ │
│ │ - 代码变更 │ │
│ │ - 资源变更 │ │
│ │ - 文件变更 │ │
│ └──────────────────────────────────────┘ │
│ ↓ │
│ 生成增量补丁包 (Delta Patch) │
│ ↓ │
│ 下载增量补丁包 │
│ ↓ │
│ 应用补丁包 │
│ ↓ │
│ 生成完整 APK │
│ ↓ │
│ 验证签名 │
│ ↓ │
│ 安装新版本 │
│ │
└─────────────────────────────────────────────────────────────┘9.2 增量更新实现
bsdiff 算法:
bsdiff 算法原理:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 对比新旧文件 │
│ ├─ 计算搜索表 │
│ ├─ 查找相同块 │
│ └─ 标记差异块 │
│ │
│ 2. 生成补丁文件 │
│ ├─ 记录相同块位置 │
│ ├─ 记录差异块内容 │
│ └─ 生成补丁数据 │
│ │
│ 3. 应用补丁文件 │
│ ├─ 读取旧文件 │
│ ├─ 应用补丁数据 │
│ └─ 生成新文件 │
│ │
└─────────────────────────────────────────────────────────────┘10. 面试考点
10.1 基础考点
1. APK 包结构?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ APK 包结构 │
│ ├─ META-INF/ # 签名目录 │
│ ├─ res/ # 资源目录 │
│ ├─ assets/ # 原始资产 │
│ ├─ classes.dex # 编译后的代码 │
│ ├─ resources.arsc # 资源映射 │
│ ├─ AndroidManifest.xml # 清单文件 │
│ └─ lib/ # 原生库 │
│ │
└─────────────────────────────────────────────────────────────┘2. 安装流程是什么?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 安装流程 │
│ ├─ 用户触发安装 │
│ ├─ PackageInstallerService 处理 │
│ ├─ PMS 解析 APK │
│ ├─ 验证签名 │
│ ├─ 分配 UID/GID │
│ ├─ 复制文件 │
│ ├─ dex2oat 编译 │
│ └─ 注册组件 │
│ │
└─────────────────────────────────────────────────────────────┘10.2 进阶考点
1. 签名验证机制?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 签名验证 │
│ ├─ 使用私钥签名 │
│ ├─ 计算 APK 摘要 │
│ ├─ 加密摘要 │
│ ├─ 验证时解密签名 │
│ ├─ 计算当前 APK 摘要 │
│ └─ 对比摘要验证完整性 │
│ │
└─────────────────────────────────────────────────────────────┘2. 权限授予流程?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 权限授予 │
│ ├─ 普通权限:安装时自动授予 │
│ ├─ 危险权限:运行时请求 │
│ ├─ 签名权限:系统签名应用 │
│ └─ 特殊权限:系统设置授予 │
│ │
└─────────────────────────────────────────────────────────────┘10.3 高级考点
1. dex2oat 优化原理?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ dex2oat 优化 │
│ ├─ 将 DEX 编译成 Native 代码 │
│ ├─ 提升执行速度 │
│ ├─ 减少启动时间 │
│ ├─ 支持多种编译模式 │
│ └─ 缓存 OAT 文件 │
│ │
└─────────────────────────────────────────────────────────────┘2. 增量更新原理?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 增量更新 │
│ ├─ 对比新旧版本差异 │
│ ├─ 生成增量补丁包 │
│ ├─ 下载增量补丁包 │
│ ├─ 应用补丁包 │
│ └─ 生成完整 APK │
│ │
└─────────────────────────────────────────────────────────────┘文档信息:
- 字数:约 13000 字
- 包含:安装方式、PackageInstallerService、PMS 解析 APK、签名验证、权限授予、dex2oat 优化、安装失败原因、增量更新、面试考点
- 源码引用:PackageParser.java、PackageSigner.java、PackageManagerService.java、PackageInstallerService.java 等
- ASCII 流程图:包含多个流程时序图和状态机图
- 最佳实践:安装优化建议、签名最佳实践、权限请求最佳实践
建议:
- 学习源码时,结合实际安装包进行调试
- 掌握不同签名方案的区别和应用场景
- 了解 dex2oat 优化原理和编译模式选择