概述:
android和你通常理解的程序路径是不一样的,android应用安装完毕后,会存储在/data/app或者/system/app目录中,当程序运行时,所需要的layout文件,drawable文件等等需要从该目录中的原文件中进行读取。首先加载resource.asc,然后根据id值寻找相应的资源,而lib目录等特殊文件会存放 /data/data/你的应用包名此路径下。
- 安装分类
- 几个相关目录
- APK安装流程
- APK解析流程
- 调用PMS
安装分类
根据触发条件可分为三类:
分类 | 说明 |
---|---|
系统应用安装 | 没有安装界面,在开机时自动完成 |
网络下载应用安装 | 没有安装界面,在应用市场完成 |
ADB命令安装 | 没有安装界面,通过命令直接安装 |
外部设备安装 | 有安装界面,通过SD卡等外部设备安装,由packageInstaller处理安装逻辑 |
几个相关目录
以下是比较关键的四个目录:
(1)system/app : 系统自带的应用程序,获得root权限才能删除
(2)data/app : 用户程序安装目录,安装时会把apk文件复制到此目录下
(3)data/data : 存放应用程序的数据
(4)data/dalvik-cache : 将apk中的dex文件安装到该目录下(dex文件是dalvik虚拟机的可执行文件,大小约为原始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/目录下创建应用程序的数据目录(以应用包名命令),用来存放应用的数据库、cache、二进制的so动态库等。资源文件还在原来的APK文件里,并没有拷贝过来
(3)解析apk的AndroidManifest.xml文件
在安装apk的过程中,会解析apk的AndroidManifest.xml文件,将apk的权限、应用包名、apk的安装位置、版本、userID等重要信息保存在/data/system/packages.xml文件中。这些操作都是在PackageManagerService中完成的,并将解析出的四大组件信息注册到PackageManagerService中。
(4)显示icon图标
应用程序经过PMS中的逻辑处理后,相当于已经注册好了,如果想要在Android桌面上看到icon图标,则需要Launcher将系统中已经安装的程序展现在桌面上。
APK解析流程
解析AndroidManifest.xml文件
APK文件里包含了一个配置文件AndroidManifest.xml,Android应用程序的解析过程就是解析这个xml文件的过程。APK解析是从PackageManagerService的scanPackageLI开始的,而该方法内部又调用的是scanPackageDirtyLI()方法,我们来看一下这个方法的实现。
public class PackageManagerService extends IPackageManager.Stub {
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
//...
// writer
synchronized (mPackages) {
// 验证已注册的ContentProvider是否有其他同名,做冲突检测。
if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
final int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
if (p.info.authority != null) {
String names[] = p.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
if (mProvidersByAuthority.containsKey(names[j])) {
PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
final String otherPackageName =
((other != null && other.getComponentName() != null) ?
other.getComponentName().getPackageName() : "?");
throw new PackageManagerException(
INSTALL_FAILED_CONFLICTING_PROVIDER,
"Can't install because provider name " + names[j]
+ " (in package " + pkg.applicationInfo.packageName
+ ") is already used by " + otherPackageName);
}
}
}
}
}
}
if (mPlatformPackage == pkg) {
//...
} else {
// This is a normal package, need to make its data directory.
dataPath = getDataPathForPackage(pkg.packageName, 0);
if (dataPath.exists()) {
//...
} else {
//invoke installer to do the actual installation
//这里创建了应用数据目录,用于存放用户数据
int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
//...
}
}
// We also need to dexopt any apps that are dependent on this library. Note that
// if these fail, we should abort the install since installing the library will
// result in some apps being broken.
if (clientLibPkgs != null) {
if ((scanFlags & SCAN_NO_DEX) == 0) {
for (int i = 0; i < clientLibPkgs.size(); i++) {
PackageParser.Package clientPkg = clientLibPkgs.get(i);
if (performDexOptLI(clientPkg, null /* instruction sets */, forceDex,
(scanFlags & SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
"scanPackageLI failed to dexopt clientLibPkgs");
}
}
}
}
// writer
synchronized (mPackages) {
//...
// 以下对四大组件进行注册
int N = pkg.providers.size();
StringBuilder r = null;
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
//注册Content Provider
mProviders.addProvider(p);
//...
}
//...
}
}
//...
}
}
scanPackageDirtyLI是一个上千行的函数,它主要完成的工作如下所示:
(1)调用PackageParser的parsePackage()方法解析AndroidMainfest.xml文件,主要包括四大组件、权限信息、用户ID,其他use-feature、shared-userId、use-library等 信息,并保存到PackageManagerService相应的成员变量中。
(2)调用签名验证方法verifySignaturesLP()进行签名验证,验证失败的无法进行安装。
(3)调用createDataDirsDirtyLI()方法创建应用目录/data/data/package,同时将APK中提取的DEX文件保存到/data/dalvik-cache中。
(4)调用performDexOptLI()方法执行dexopt操作。
解析 APK 文件
Apk的解析是PackageParser的parsePackage()函数来完成的,我们来看看它的实现。
public class PackageParser {
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
//...
//初始化AssetManager
final AssetManager assets = new AssetManager();
try {
//...
//解析Base APk,解析AndroidManifest.xml
final Package pkg = parseBaseApk(baseApk, assets, flags);
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
//如果splitName不为空,则循环解析Split Apk
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
for (int i = 0; i < num; i++) {
//解析
parseSplitApk(pkg, i, assets, flags);
}
}
pkg.setCodePath(packageDir.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
}
注:Split APK是Google为解决65535上限以及APK越来越大的问题而提出的一种方案,它可以将一个庞大的APK按照屏幕密度、ABI等形式拆分成多个独立的APK,这些APK共享相同的data、cache目录。 共享相同的进程,共享相同的包名。它们还可以使用各自的资源,并且继承了Base APK里的资源。
该方法调用parseBaseApk()去解析AndroidManifest.xml,AndroidManifest.xml也是xml文件,当然也使用XmlResourceParser来解析。这个解析相应标签并保存到PackageManagerService对应的成员变量中去。
调用PMS流程
在应用中,我们通常是调用Context的getPackageManager()方法返回PackageManager对象,这里返回的实际是ApplicationPackageManager对象,这个对象创建时使用IPackageManager对象作为参数,IPackageManager是PackageManagerService实现的AIDL接口,通过binder来获取PackageManagerService对象。
因此ApplicationPackageManager是PackageManagerService的代理对象,ApplicationPackageManager继承自PackageManager,PackageManager中定义了可以操作PackageManagerService的接口,在PackageManager中,实现对apk安装的方法是installExistingPackage和installPackageWithVerificationAndEncryption(前者是通过包名去升级应用,后者是安装新的应用)
尊重作者,尊重原创,参考文章:
https://blog.csdn.net/wy391920778/article/details/79881142
https://blog.csdn.net/qq_25804863/article/details/48565569