virtualapp安装应用流程源码分析
1. HomeActivity 为处理的入口
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && data != null) {
List<AppInfoLite> appList = data.getParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST);
if (appList != null) {
for (AppInfoLite info : appList) {
mPresenter.addApp(info);
}
}
}
}
调用了 mPresenter.addApp, 这里还是使用了一个MVP的设计模式,对应的是HomePresenterImpl.java
@Override
public void addApp(AppInfoLite info) {
class AddResult {
private PackageAppData appData;
private int userId;
private boolean justEnableHidden;
}
AddResult addResult = new AddResult();
VUiKit.defer().when(() -> {
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);
addResult.justEnableHidden = installedAppInfo != null;
if (addResult.justEnableHidden) {
int[] userIds = installedAppInfo.getInstalledUsers();
int nextUserId = userIds.length;
/*
Input : userIds = {0, 1, 3}
Output: nextUserId = 2
*/
for (int i = 0; i < userIds.length; i++) {
if (userIds[i] != i) {
nextUserId = i;
break;
}
}
addResult.userId = nextUserId;
if (VUserManager.get().getUserInfo(nextUserId) == null) {
// user not exist, create it automatically.
String nextUserName = "Space " + (nextUserId + 1);
VUserInfo newUserInfo = VUserManager.get().createUser(nextUserName, VUserInfo.FLAG_ADMIN);
if (newUserInfo == null) {
throw new IllegalStateException();
}
}
boolean success = VirtualCore.get().installPackageAsUser(nextUserId, info.packageName);
if (!success) {
throw new IllegalStateException();
}
} else {
InstallResult res = mRepo.addVirtualApp(info);
if (!res.isSuccess) {
throw new IllegalStateException();
}
}
}).then((res) -> {
addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
}).done(res -> {
boolean multipleVersion = addResult.justEnableHidden && addResult.userId != 0;
if (!multipleVersion) {
PackageAppData data = addResult.appData;
data.isLoading = true;
mView.addAppToLauncher(data);
handleOptApp(data, info.packageName, true);
} else {
MultiplePackageAppData data = new MultiplePackageAppData(addResult.appData, addResult.userId);
data.isLoading = true;
mView.addAppToLauncher(data);
handleOptApp(data, info.packageName, false);
}
});
}
这里有一行
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);
本质是去访问自定义的VAppManagerService
public InstalledAppInfo getInstalledAppInfo(String packageName, int flags) {
synchronized (PackageCacheManager.class) {
if (packageName != null) {
PackageSetting setting = PackageCacheManager.getSetting(packageName);
if (setting != null) {
return setting.getAppInfo();
}
}
return null;
}
}
这里是从自己定义的包缓存信息中查询是否有安装过这个包,(后面再分析是怎么记录安装信息的)
显然现在还没有安装过这个包,那么会走到else里,即执行InstallResult res = mRepo.addVirtualApp(info);
AppRepository.java
@Override
public InstallResult addVirtualApp(AppInfoLite info) {
int flags = InstallStrategy.COMPARE_VERSION | InstallStrategy.SKIP_DEX_OPT;
if (info.fastOpen) {
flags |= InstallStrategy.DEPEND_SYSTEM_IF_EXIST;
}
return VirtualCore.get().installPackage(info.path, flags);
}
VirtualCore.java
public InstallResult installPackage(String apkPath, int flags) {
try {
return getService().installPackage(apkPath, flags);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
那么这里在借助aidl RPC的能力 ,利用VAppManagerService来安装包
VAppManagerService.java(方法有点大,会删除非核心逻辑的代码)
public synchronized InstallResult installPackage(String path, int flags, boolean notify) {
...
// 真实的apk文件路径
File packageFile = new File(path);
if (!packageFile.exists() || !packageFile.isFile()) {
return InstallResult.makeFailure("Package File is not exist.");
}
VPackage pkg = null;
try {
// 解析apk.生成VPackage对象
pkg = PackageParserEx.parsePackage(packageFile);
} catch (Throwable e) {
e.printStackTrace();
}
...
InstallResult res = new InstallResult();
res.packageName = pkg.packageName;
...
// 生成新的安装目录
//地址大概是/data/data/io.virtualapp/virtual/data/app/应用包名/
File appDir = VEnvironment.getDataAppPackageDirectory(pkg.packageName);
// 生成新的so安装目录
File libDir = new File(appDir, "lib");
...
// 判断是不是外部安装,比如sdcard安装
boolean dependSystem = (flags & InstallStrategy.DEPEND_SYSTEM_IF_EXIST) != 0
&& VirtualCore.get().isOutsideInstalled(pkg.packageName);
....
// 把原应用的lib下的数据copy过来
NativeLibraryHelperCompat.copyNativeBinaries(new File(path), libDir);
if (!dependSystem) {
//假设是基于手机已安装的应用安装,那么就是走的这里
File privatePackageFile = new File(appDir, "base.apk");
File parentFolder = privatePackageFile.getParentFile();
if (!parentFolder.exists() && !parentFolder.mkdirs()) {
VLog.w(TAG, "Warning: unable to create folder : " + privatePackageFile.getPath());
} else if (privatePackageFile.exists() && !privatePackageFile.delete()) {
VLog.w(TAG, "Warning: unable to delete file : " + privatePackageFile.getPath());
}
try {
//把原package的数据copy过来
FileUtils.copyFile(packageFile, privatePackageFile);
} catch (IOException e) {
privatePackageFile.delete();
return InstallResult.makeFailure("Unable to copy the package file.");
}
packageFile = privatePackageFile;
}
if (existOne != null) {
PackageCacheManager.remove(pkg.packageName);
}
//修改新路径的权限
chmodPackageDictionary(packageFile);
// 生成新包安装的配置信息,用于持久化和后续查询用
PackageSetting ps;
if (existSetting != null) {
ps = existSetting;
} else {
ps = new PackageSetting();
}
ps.dependSystem = dependSystem;
ps.apkPath = packageFile.getPath();
ps.libPath = libDir.getPath();
ps.packageName = pkg.packageName;
//这里为这个app生成一个appId
ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
if (res.isUpdate) {
ps.lastUpdateTime = installTime;
} else {
ps.firstInstallTime = installTime;
ps.lastUpdateTime = installTime;
for (int userId : VUserManagerService.get().getUserIds()) {
boolean installed = userId == 0;
ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
}
}
//在/data/data/io.virtualapp/virtual/data/app/应用包名/下持久化一个 package.ini,用于记录VPackage的信息, 下次读取可以直接用
PackageParserEx.savePackageCache(pkg);
//缓存一下信息
PackageCacheManager.put(pkg, ps);
mPersistenceLayer.save();
if (!dependSystem) {
boolean runDexOpt = false;
if (VirtualRuntime.isArt()) {
try {
ArtDexOptimizer.interpretDex2Oat(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath());
} catch (IOException e) {
e.printStackTrace();
runDexOpt = true;
}
} else {
runDexOpt = true;
}
if (runDexOpt) {
try {
DexFile.loadDex(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath(), 0).close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BroadcastSystem.get().startApp(pkg);
if (notify) {
notifyAppInstalled(ps, -1);
}
res.isSuccess = true;
return res;
}
可以看到,在VA内部安装虚拟应用,VA主要做了这几件事
- 反射创建android.pm.PackageParser实例,解析虚拟应用apk包的四大组件以及其他信息;
- 把so库复制到对应包的虚拟路径下;
- 保存、持久化部分apk包数据到硬盘内;
- 把apk包复制到对应的虚拟路径下;
virtualapp安装应用流程源码分析的更多相关文章
- [Android]Android系统启动流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...
- Spark(四十九):Spark On YARN启动流程源码分析(一)
引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...
- [Android]从Launcher开始启动App流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...
- Spring加载流程源码分析03【refresh】
前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...
- Android笔记--View绘制流程源码分析(二)
Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...
- Android笔记--View绘制流程源码分析(一)
Android笔记--View绘制流程源码分析 View绘制之前框架流程分析 View绘制的分析始终是离不开Activity及其内部的Window的.在Activity的源码启动流程中,一并包含 着A ...
- Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)
上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...
- spring boot 加载web容器tomcat流程源码分析
spring boot 加载web容器tomcat流程源码分析 我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>o ...
- springboot 事务创建流程源码分析
springboot 事务创建流程源码分析 目录 springboot 事务创建流程源码分析 1. 自动加载配置 2. InfrastructureAdvisorAutoProxyCreator类 3 ...
- 5.Spark Streaming流计算框架的运行流程源码分析2
1 spark streaming 程序代码实例 代码如下: object OnlineTheTop3ItemForEachCategory2DB { def main(args: Array[Str ...
随机推荐
- 【转帖】mysql一个索引块有多少指针_深刻理解MySQL系列之索引
索引 查找一条数据的过程 先看下InnoDB的逻辑存储结构:node 表空间:能够看作是InnoDB存储引擎逻辑结构的最高层,全部的数据都存放在表空间中.默认有个共享表空间ibdata1.若是启用in ...
- DBeaver连接国产信创数据库的步骤
DBeaver连接国产信创数据库的步骤 本次连接使用的数据库类型 1.达梦 2.神通 3.人大金仓 4.瀚高 安装DBeaver 通过官网或者是其他网站下载最新的数据库介质 之后的操作为: 这次不感谢 ...
- Springboot 数据库连接池大小简单总结
最近在进行性能压测, 想验证一下产品的极限性能, 在使用openpower 2路22核(SMT4)176线程 512G内存的服务器上面进行性能压测 压测进行到1000并发或者是2000并发时性能有一定 ...
- linux机制
cpu Cache 工作原理:文中对Cache的一致性提出了两种策略:基于监听的和基于目录的.前者是所有Cache均监听各个Cache的写操作,当一个Cache中的数据被写了,其处理方式有:写更新协议 ...
- Spring Cloud 系列:基于Seata 实现 XA模式
https://seata.io/zh-cn/docs/user/mode/xa https://seata.io/zh-cn/docs/dev/mode/xa-mode XA 规范 是 X/Open ...
- STM32CubeMX教程25 PWR 电源管理 - 睡眠、停止和待机模式
1.准备材料 开发板(正点原子stm32f407探索者开发板V2.4) STM32CubeMX软件(Version 6.10.0) 野火DAP仿真器 keil µVision5 IDE(MDK-Arm ...
- typeScript类型别名
类型别名 类型别名:是可以给一个类型起一个新的名字 采用关键字 type 例如 type Name=string|number type strType=string|number|boolean; ...
- 【K哥爬虫普法】百度、360八年恩怨情仇,robots 协议之战终落幕
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识,知 ...
- 飞桨paddle遇到bug调试修正【迁移工具、版本兼容性】
PaddlePaddlle强化学习及PARL框架{飞桨} [一]-环境配置+python入门教学 [二]-Parl基础命令 [三]-Notebook.&pdb.ipdb 调试 [四]-强化学习 ...
- trick1---实现tensorflow和pytorch迁移环境教学
相关文章: [一]tensorflow安装.常用python镜像源.tensorflow 深度学习强化学习教学 [二]tensorflow调试报错.tensorflow 深度学习强化学习教学 [三]t ...