最近有这样的一个需求:有多个APK(均为二进制文件),测试人员在使用时需要批量安装和授权。由于授权时需要人工点击多个apk,说是很麻烦。因此需要在不修改APK的情况下,自动完成这些APK的安装和授权。

leader提了两个思路:

  • 思路1: 使用一个host APK来加载这些APK,并在host APK中实现APK的调度。
  • 思路2: 使用virtual APP

思路1

思路1类似于APK开发时的插件化技术,核心思想即需要时动态加载这些APK。插件化技术业内比较知名的一个实现是RePlugin。该项目可以将APK转化成内置插件和外部插件。存在两个问题:

  1. 但是无论是内置插件和外置插件,都需要按照接入指南中的要求,修改build.gradle文件,添加相应的依赖。由于我们不能修改APK的二进制文件,因此这种思路就失败了。
  2. 安装APK时,由于package name不一定一致,因此没有办法在host APK中没办法替子APK申请权限。子APK需要权限时还是要再申请。

思路2

virtual APP的核心思想是虚拟化,提供APK运行时需要的所有环境,类似于一个APK虚拟机。这时这些APK安装时和virtual APK的uid是一致的,因此不会受到Android系统的限制。关于原理,知乎上的@wakao的回答说的很好:

一个apk能够运行起来,关键在于有能够运行app的环境,va做的就是这个事情--构造一个能够运行app的环境。那这个环境是什么,代码上是framework,实际是android的一个核心进程是system server(system_process)。启动system server,会启动一系列一系列的核心服务,众所周知的例如ams,wms,pms等等等。va要做的就是虚拟一个system_process进程,里面也有这一系列的核心服务,也可以认为这个是system_process的一个影射。从代码上就知道,lody实现上,甚至是连函数的命名(例如各个service的systemReady方法),执行次序等都完全参照framework的代码,保证实现上接近system_process。

作者:wakao
链接:https://www.zhihu.com/question/48269910/answer/185062260

关于virtual APP,github上已经不再更新,下载编译完成后再android 9上不能导入APK。这里找到了一个适配android 9的。https://blog.csdn.net/XXOOYC/article/details/82388477

利用virtual APP,我们可以实现在一个APP里面安装其他的多个APP,经过测试,只需要把权限给virtual APP即可,里面的APK自动获取相应的权限。因此,是可以满足我们的需求的。

修改部分说明

修改后的代码可以参考这里:https://github.com/thinkycx/VirtualAppEx

对于原先代码,结合需求,我做了如下的修改:

  1. 使用sharedPreferences技术创建xml文件,记录安装次数,保证只安装一次。
  2. 将asserts/下的apk文件复制到/data/data/<package.name>/files目录下。(asserts下的文件我无法获得绝对路径,因此只能复制到别的目录下来获取path)
  3. 修改ListAppFragment的onViewCreated方法,指定apk的package name和path实现apk的安装。
  4. 删除不需要的操作,如:askInstallGms。(检测google service)

备注

  1. 原先安装apk时的调用链:ListAppActivity->ListAppFragement->onViewCreated->mInstallButton.setOnClickListener()->dataList.add()。
  2. 原先的修改方式是在HomeActivity的initLaunchpad方法中调用dataList.add()。但是,这种实现只能安装好apk,HomeActivity的View没有刷新,重新启动virtual APP才可以看见其中安装的apk。调试了很久还是不解决,于是只好恢复add——app这个button,在里面实现安装逻辑。
  3. 如果需要安装新的apk,需要:1)复制apk到asserts/目录下 2)增加ListAppFragement中的dataList.add()方法,指定apk package name和复制之后的path即可。