virtualapp安装应用流程源码分析
Posted 明月照江江的技术博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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包复制到对应的虚拟路径下;
Android 插件化VirtualApp 源码分析 ( 启动应用源码分析 | HomePresenterImpl 启动应用方法 | VirtualCore 启动插件应用最终方法 )
一、启动应用源码分析
1、HomeActivity 启动应用点击方法
在 io.virtualapp.home.HomeActivity
页面中 , 点击列表中的应用后 , 可以启动应用 ;
直接到 io.virtualapp.home.adapters.LaunchpadAdapter
适配器中查找点击事件源码 , 调用的是 private OnAppClickListener mAppClickListener;
成员变量的 onAppClick
方法 ;
该成员变量是通过 public void setAppClickListener(OnAppClickListener mAppClickListener)
方法注入的 ;
public class LaunchpadAdapter extends RecyclerView.Adapter<LaunchpadAdapter.ViewHolder> {
private LayoutInflater mInflater;
private List<AppData> mList;
private SparseIntArray mColorArray = new SparseIntArray();
private OnAppClickListener mAppClickListener;
public void setAppClickListener(OnAppClickListener mAppClickListener) {
this.mAppClickListener = mAppClickListener;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemView.setBackgroundColor(holder.color);
holder.itemView.setOnClickListener(v -> {
if (mAppClickListener != null) {
mAppClickListener.onAppClick(position, data);
}
});
}
}
2、HomePresenterImpl 启动应用方法
调用 LaunchpadAdapter.setAppClickListener
方法注入应用启动点击事件 ,
public class HomeActivity extends VActivity implements HomeContract.HomeView {
private static final String TAG = HomeActivity.class.getSimpleName();
private HomeContract.HomePresenter mPresenter;
private LaunchpadAdapter mLaunchpadAdapter;
private void initLaunchpad() {
mLaunchpadAdapter.setAppClickListener((pos, data) -> {
if (!data.isLoading()) {
if (data instanceof AddAppButton) {
onAddAppButtonClick();
}
mLaunchpadAdapter.notifyItemChanged(pos);
mPresenter.launchApp(data);
}
});
}
}
应用启动 , 实际上调用的是 io.virtualapp.home.HomePresenterImpl
的 public void launchApp(AppData data)
方法 ;
class HomePresenterImpl implements HomeContract.HomePresenter {
@Override
public void launchApp(AppData data) {
try {
if (data instanceof PackageAppData) {
PackageAppData appData = (PackageAppData) data;
Log.i("HSL", "PackageAppData : " + appData.toString());
appData.isFirstOpen = false;
LoadingActivity.launch(mActivity, appData.packageName, 0);
} else if (data instanceof MultiplePackageAppData) {
MultiplePackageAppData multipleData = (MultiplePackageAppData) data;
Log.i("HSL", "MultiplePackageAppData : " + multipleData.toString());
multipleData.isFirstOpen = false;
LoadingActivity.launch(mActivity, multipleData.appInfo.packageName, ((MultiplePackageAppData) data).userId);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
拦截一下数据 , 上述两个位置打印出来的日志如下 :
I/HSL: PackageAppData : PackageAppData{packageName='kim.hsl.svg', name='SVG', icon=android.graphics.drawable.BitmapDrawable@8d312bf, fastOpen=false, isFirstOpen=false, isLoading=false}
3、VirtualCore 启动插件应用最终方法
最终启动应用的方法是 io.virtualapp.home.LoadingActivity
类中的 public static void launch(Context context, String packageName, int userId)
方法 ;
先从 VirtualCore
中获取启动的 Intent 1
, 然后启动一个 VActivity
, 创建 intent 2
, 将 从 VirtualCore
中获取启动的 Intent
放到 intent 2
中 ;
public class LoadingActivity extends VActivity {
private static final String PKG_NAME_ARGUMENT = "MODEL_ARGUMENT";
private static final String KEY_INTENT = "KEY_INTENT";
private static final String KEY_USER = "KEY_USER";
private PackageAppData appModel;
public static void launch(Context context, String packageName, int userId) {
Intent intent = VirtualCore.get().getLaunchIntent(packageName, userId);
Log.i("HSL", "packageName : " + packageName + " , userId : " + userId);
if (intent != null) {
Intent loadingPageIntent = new Intent(context, LoadingActivity.class);
loadingPageIntent.putExtra(PKG_NAME_ARGUMENT, packageName);
loadingPageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
loadingPageIntent.putExtra(KEY_INTENT, intent);
loadingPageIntent.putExtra(KEY_USER, userId);
context.startActivity(loadingPageIntent);
}
}
}
打印的数据 :
I/HSL: packageName : kim.hsl.svg , userId : 0
以上是关于virtualapp安装应用流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 安装应用源码分析 | HomePresenterImpl 添加应用 | AppRepository.addVirtualApp )
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 插件化VirtualApp 源码分析 ( 添加应用源码分析 | LaunchpadAdapter 适配器 | 适配器添加元素 | PackageAppData 元素 )
Android 插件化VirtualApp 源码分析 ( 添加应用源码分析 | LaunchpadAdapter 适配器 | 适配器添加元素 | PackageAppData 元素 )(代
Android 插件化VirtualApp 源码分析 ( 启动应用源码分析 | HomePresenterImpl 启动应用方法 | VirtualCore 启动插件应用最终方法 )