Android 安装apk流程,Android PMS(二)-Apk安装流程

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

一、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是系统内置的应用程序,用于安装和卸载应用程序。

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

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

3c2ed166e829

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

3c2ed166e829

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

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处理安装消息

3c2ed166e829

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

3c2ed166e829

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

3) 安装APK

3c2ed166e829

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等等。

3c2ed166e829

PackageParser主要负责解析AndroidManifest

总结下installPackageLI过程:

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

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

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

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

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

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

3c2ed166e829

参考:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值