Android虚拟机使用整理

虚拟机其实我们并不陌生,比如vmware,virtualbox等PC平台上的虚拟机软件,相信很多人都玩过。比如在windows上跑一个ubuntu来研究一下linux,编译一下android源码什么的。使用虚拟机一般有两个目的,一个是平台模拟,一个是便于维护。

我们知道系统的底层是基于CPU设计的硬件平台,而CPU根据应用场景不同有着不同的指令集和寄存器结构,也就是架构。常见的PC平台有x86、x86_64架构,移动平台有arm架构。这些架构的差异使得不同的系统无法直接跨平台运行,因为系统源码最终要被编译成机器指令才能运行。当然有些系统支持了非常丰富的硬件平台,比如Linux,既可以在x86平台运行,也可以在arm平台上运行。但是如果我们要在x86平台上运行最初只支持arm结构的Android系统,我们就需要一个中间层,模拟出一个arm平台,将arm架构的指令和寄存器操作,使用软件功能来实现。有的虚拟机为了提高执行效率,也有将目标平台的指令,直接翻译成宿主平台指令执行的,具体一点可以参考这里,虚拟机的原理大致如下图:

虚拟机原理示意图

我们说的平台模拟的虚拟机就属于TYPE2类型,底层的OS就是宿主系统,上层的OS就是目标系统,中间的HYPER VISOR就是虚拟机软件。Android虚拟机也是平台虚拟机的一种,为了后续方便研究,我们先总结一下PC上虚拟机的运行过程。首先我们需要下载一个虚拟机软件,然后在软件里创建虚拟机,指定虚拟的硬件配置,虚拟机数据会存储在一个虚拟磁盘文件里,最后我们需要为虚拟机安装目标系统,然后就可以启动虚拟机了。所以它包含几个主要部分:

  • 虚拟机软件
  • 硬件配置
  • 虚拟机文件
  • 目标系统

虚拟机和真机差异

我们运行自己APP,既可选择真机调试运行,也可以使用虚拟机。一般情况下,使用真机速度更快,体验更佳,但是真机也有自己的一些缺陷。比如,获取成本高,假如要测试APP的兼容性,针对不同API版本的机型都进行配置的话,将会是一笔不少的花费。其次,真机的系统是经过OEM厂家定制的,不一定是原生的API和内在逻辑,也无法针对GoogleAPI等服务进行开发,如果机器里没有的话。特别地,当你想要烧录自己编译的源码,对FrameWork或者更加底层的逻辑行调试的时候,真机则更加不方便。OEM厂家一般都会锁定刷机功能,比如FastBoot,只能用厂家自己的刷机工具进行刷机,一般也不容获取。并且每家的源码和镜像文件格式都不相同,编译的标准源码无法直接烧录。所以,如果要进行Android系统学习,学习虚拟机的使用还是非常必要的。

使用方式及差异

Android虚拟机的获取方式有两种,一种是下载AndroidStudio,使用AndroidStudio进行虚拟机的创建,下载对应的系统镜像,虚拟机运行之后就可以当做设备一样使用adb连接调试了。还有一种是下载Android sdk tool,使用avd manager命令进行安装、运行。第一种的优势是方便、直观,根据UI提示一步一步操作就可以创建、运行虚拟机,实际上它也是使用avd manager的功能。第二种的优势是可以了解adv manager的工作原理,可以使用参数进行最灵活的配置,可以定制自己的kernel、system镜像等等。我们要研究的就是第二种。

从官方的教程来看,Android虚拟机是基于QEMU开源虚拟机衍生出来的,如果想深入研究一下虚拟机原理,可以研究一下QEMU。如果要看官方教程请戳这里,QEMU官方教程请戳这里

虚拟机的安装

官方链接上,我们可以下载到不同平台的AndroidStudio,其中自带了整套的AndroidSdk工具。如果我们不需要AndroidStudio,比如在构建服务器上,我们也可以只下载sdk工具,通过工具在线下载build tool、不同版本的sdk、创建虚拟机等等。这里不做详细的介绍。

安装完之后,将SDK路径/tools添加到PATH环境变量中,便于使用。也可以直接到sdk路径下面运行命令,MAC平台的SDK路径是:

/Users/your_name/Library/Android/sdk/

虚拟机的运行

使用avdmanager命令行创建虚拟机比较复杂,也是理解虚拟机的核心部分,我们稍后一步一步探索。

假如我们有一个创建好的虚拟机,比如我们可以先使用AndroidStudio创建一个,就可以使用以下的命令直接运行虚拟机。

emulator -avd avd_name [ {-option [value]} … ]

或者

emulator @avd_name [ {-option [value]} … ]

可以看到这个emulator就是我们虚拟机程序正主了,我们到sdk安装目录下去看看它是个什么文件。

heavy:~/Library/Android/sdk/tools$ file emulator
emulator: Mach-O 64-bit executable x86_64
heavy:~/Library/Android/sdk/tools$ ls -alh emulator
-rwxr-xr-x  1 heavy  staff   253K  1  2  2018 emulator

可以看到,emulator是一个可执行文件,但是体积很小,按照虚拟机的功能和复杂性而言,应该是不只这个大小的,所以合理推测它应该还依赖了其他的可执行文件或者库文件,这里先不研究,就把它当做虚拟机程序。

虚拟机的存储路径

根据PC虚拟机使用总结,虚拟机要运行还需要安装了目标系统的虚拟磁盘文件,这里通过-avd参数的名称指定了磁盘文件位置,我们来看看虚拟机是怎么根据名称来找到这个文件的。

我们执行以下命令:

./bin/avdmanager list avd

可以看到输出:

Parsing /Users/heavy/Library/Android/sdk/build-tools/21.1.1/package.xml
...
...
Parsing /Users/heavy/Library/Android/sdk/system-images/android-23-bak/google_apis/x86_64/package.xml
...
    Name: Nexus_5X_API_23
  Device: Nexus 5X (Google)
    Path: /Users/heavy/.android/avd/Nexus_5X_API_23.avd
  Target: Google APIs (Google Inc.)
          Based on: Android 6.0 (Marshmallow) Tag/ABI: google_apis/x86_64
    Skin: nexus_5x
  Sdcard: 512M

avdmanager解析了很多的平台配置文件:package.xml,最后告诉我们找到一个可用的虚拟机,位置在主目录下面。我们打开文Nexus_5X_API_23.avd件查看内容:

avd.ini.encoding=UTF-8
path=/Users/heavy/.android/avd/Nexus_5X_API_23.avd
path.rel=avd/Nexus_5X_API_23.avd
target=android-23

发现它只是一个配置文件,通过path和path.rel指向虚拟机数据文件夹的绝对、相对地址。我们再查看目录的内容:

drwxr-xr-x  18 heavy  staff   576B 12  4 10:24 .
drwxr-xr-x   4 heavy  staff   128B 12  4 10:34 ..
-rw-r--r--   1 heavy  staff    66M 12  3 14:25 cache.img
-rw-r--r--   1 heavy  staff   4.3M 12  4 10:24 cache.img.qcow2
-rw-r--r--   1 heavy  staff   1.1K 12  3 14:25 config.ini
drwxr-xr-x   7 heavy  staff   224B 12  3 14:25 data
-rw-r--r--   1 heavy  staff    51B 12  4 10:24 emulator-user.ini
-rw-r--r--   1 heavy  staff   1.0M 12  3 14:25 encryptionkey.img
-rw-r--r--   1 heavy  staff   448K 12  4 10:24 encryptionkey.img.qcow2
-rw-r--r--   1 heavy  staff   1.9K 12  4 10:24 hardware-qemu.ini
-rw-r--r--   1 heavy  staff   512M 12  3 14:25 sdcard.img
-rw-r--r--   1 heavy  staff   768K 12  4 10:24 sdcard.img.qcow2
drwxr-xr-x   3 heavy  staff    96B 12  3 14:26 snapshots
-rw-r--r--   1 heavy  staff   192K 12  3 14:25 system.img.qcow2
-rw-r--r--   1 heavy  staff   2.0G 12  3 14:25 userdata-qemu.img
-rw-r--r--   1 heavy  staff   326M 12  4 10:24 userdata-qemu.img.qcow2
-rw-r--r--   1 heavy  staff   2.0G 12  3 14:25 userdata.img
-rw-r--r--   1 heavy  staff     8B 12  3 14:25 version_num.cache

我们就找到了虚拟机使用的相关数据了,具体的文件内容稍后研究。现在我们回头来看看,avdmanager是怎么找到Nexus_5X_API_23.avd文件的,为什么查找虚拟机要解析那些package.xml文件呢。

根据官网的描述,虚拟机数据路径默认规则是:

~/.android/avd/name.avd/

其中name就是虚拟机的名称, ~是主机上的android主目录,可以通过ANDROID_SDK_HOME来动态指定。

it displays a list of AVD names from your Android home directory. Note that you can override the default home directory by setting the ANDROID_SDK_HOME environment variable.

如果设置环境变量指向一个其他的路径,avdmanager就会到指定的路径下去寻找虚拟机文件,下面这个api26是手动拷贝到环境变量指定的路径下面的,但是它会被当做一个虚拟机。

heavy:~/Library/Android/sdk/tools$ echo $ANDROID_SDK_HOME
/Users/heavy/temp
heavy:~/Library/Android/sdk/tools$ ls ~/temp/.android/avd/
Nexus_5X_API_26.ini
heavy:~/Library/Android/sdk/tools$ ./bin/avdmanager list avd
Virtual Devices:
    Name: Nexus_5X_API_26
  Device: Nexus 5X (Google)
    Path: /Users/heavy/.android/avd/Nexus_5X_API_23.avd
  Target: Google APIs (Google Inc.)
          Based on: Android 6.0 (Marshmallow) Tag/ABI: google_apis/x86_64
    Skin: nexus_5x
  Sdcard: 512M

如果我们修改了虚拟机存储路径的名称:

heavy:~/.android/avd$ mv Nexus_5X_API_23.avd/ Nexus_5X_API_23.avd-bak
heavy:~/.android/avd$ ~/Library/Android/sdk/tools/bin/avdmanager list avd
Available Android Virtual Devices:

The following Android Virtual Devices could not be loaded:
    Name: Nexus_5X_API_23
    Path: /Users/heavy/.android/avd/Nexus_5X_API_23.avd
   Error: Failed to parse properties from /Users/heavy/.android/avd/Nexus_5X_API_23.avd/config.ini

avdmanager依然可以找到虚拟机,但是加载的时候会失败,如果我们修改了虚拟机初始化文件的路径,avdmanager直接就找不到虚拟机了。

heavy:~/.android/avd$ ls
heavy:~/.android/avd$ mv Nexus_5X_API_23.ini Nexus_5X_API_23.ini-bak
heavy:~/.android/avd$ ls
Nexus_5X_API_23.avd     Nexus_5X_API_23.ini-bak
heavy:~/.android/avd$ ~/Library/Android/sdk/tools/bin/avdmanager list avd
Available Android Virtual Devices:

说明实际上avdmanager是根据路径规则,找的同名配置文件,然后根据配置文件里的路径,再寻找虚拟机的数据路径的。那为什么要解析sdk路径下面的package.xml文件呢?

这里我没有找到足够的资料,只能做一个推测。我们知道AndroidSDK工具分了很多平台和api版本,除去最基本的tools,plaform-tools等工具是sdk必备的,其他的都是可以根据需要选装的,所以google提供了sdkmanager作为一个工具,用于管理本地的sdk工具集,包括从远程仓库下载和安装不同版本的sdk、编译好的虚拟机系统镜像等等。为了方便安装使用,sdkmanager的实现,没有使用数据库这种强关系管理工具来管理平台和依赖,而只是使用环境变量、目录结构这种弱关系,比如允许ANDROID_SDK_HOME来指定android主目录,比如sdk版本默认放在sdk/platforms下面而虚拟机系统镜像放在sdk/system-imges下面。出于灵活性考虑,sdkmanager对sdk版本和系统镜像版本的目录结构不是在代码里写死的,而是通过package.xml索引文件来声明的,包括包类型,api版本,显示名称,相对路径等等。所以当我们avdmanager找到了虚拟机文件的时候,还要根据虚拟机的平台、版本,查看是否有安装对应的系统镜像,所以就有了上述的package.xml遍历。

为了验证这个推测,我们把sdk/platforms/下,android-21和android-27重命名为android-210,android-270:

heavy:~/Library/Android/sdk/platforms$ ls
android-19  android-22  android-24  android-26
android-210 android-23  android-25  android-270

然后把对应目录下package.xml中的path路径也修改为210和270,打开sdkmanager,查看安装的sdk版本:

路径修改后sdk版本列表

可以看到修改后的sdk版本还是可以被正确地识别,说明我们的推测是对的。但是avdmanager为什么要搜索已安装的sdk和system-images版本还是没有看懂。实际上在虚拟机目录下的hardware-qemu.ini文件里,有关于虚拟机的所有配置,其中就包括了kernel,system等镜像文件的路径,是不需要进行搜索查找的。

我们总结一下虚拟机文件的查找路径:

  1. 根据$ANDROID_SDK_HOME/.android/avd/name.ini获取虚拟机列表,如果环境变量未设置,默认在用户的主目录
  2. 根据name.ini里的PATH,查找虚拟机数据的存放路径
  3. 虚拟机使用的数据文件路径、配置等由路径下的hardware-qemu.ini文件指定

虚拟机的文件内容

在上面的存储路径的研究中,我们已经看到了虚拟机的文件列表,官方对于这些也没有非常详细的说明,只说明了几个比较关键的文件,在这里尽量说明个人理解的所有内容,包括一些推测。

  • cache.img
    缓存分区,会挂载在虚拟机的/cache目录下,用于存储下载等一些临时文件,关机的时候会被清除,可以在虚拟机运行的时候,使用参数-cache持久化缓存。缓存分区开始是空的,并且会被清空如果使用了-wipe-data选项。
  • cache.img.qcow2
    cache.img的软连接
  • config.ini
    设备配置文件,用于Android设备相关的属性。
  • data
    虚拟机运行的一些数据
  • emulator-user.ini
    未知
  • encryptionkey.img
    不清楚具体作用,等研究完android源码构建之后,再行补充。
  • encryptionkey.img.qcow2
    encryptionkey的软连接
  • hardware-qemu.ini
    设备配置文件,用于虚拟机自身相关的属性,包括文件系统镜像等。
  • sdcard.img
    sd卡分区,用于模拟设备的sd卡。
  • sdcard.img.qcow2
    sdcard.img的软连接。
  • snapshots
    快照数据路径,用于保存虚拟机的运行状态,快速恢复虚拟机。
  • system.img.qcow2
    system.img的软连接,因为system.img是只读的,所以并没有被拷贝到虚拟机目录下。
  • userdata.img
    系统文件下拷贝过来,用于生成userdata-qemu.img文件的,不知道为啥之后没有删除。
  • userdata-qemu.img
    /data分区的镜像文件,每个虚拟机单独一个,在虚拟机创建或者使用-wipe-data选项的时候,根据系统目录下的userdata.img文件生成。
  • userdata-qemu.img.qcow2
    userdata-qemu.img的软连接
  • version_num.cache
    未知

以上是虚拟机创建后的数据文件,还有几个文件是放在系统镜像文件下面,直接通过配置文件索引的:

  • kernel-qemu or kernel-ranchu
    系统内核镜像
  • ramdisk.img
    /boot分区的镜像,是system.img之前挂载,用于做一些初始化工作

虚拟机的配置

上述已经介绍了虚拟机的两个配置文件hardware-qemu.ini和config.ini,用于设置虚拟机设备和Android设备属性。一般来说通过这些配置模拟市面上真机的配置,不是特殊需要,不要进行修改。但是hardware-qemu.ini中指定了虚拟机的系统分区镜像文件的路径,如果进行源码调试,可以直接修改到源码编译的目录,这样避免了编译结束的拷贝动作。具体的配置请参考配置文件,还是比较直观的。

创建虚拟机

执行以下命令可以创建一个虚拟机:

avdmanager create avd -n TEST_AVD -c 512M -k "system-images;android-23;google_apis;x86_64"

执行后avdmanger会让你输入虚拟机的各项配置,尝试一路默认选项之后,也可以运行起来,这里就不再贴了。这些参数很多,理解起来也很费劲,没弄好的话后面用起来出啥问题也了不一定,推荐还是用UI工具来创建虚拟机会更加方便友好一点。

这里重点想讲一下 -k 参数,一开始没有研究明白的时候,总看不懂这个参数的含义,也就看不懂这条命令。这个参数用分号隔开的,其实相对sdk目录的,虚拟机初始镜像的存放的路径,用avdmanager UI工具下载的系统镜像都按照如下的规则存储:

~/Library/Android/sdk/system-images/android-apiLevel/variant/arch/

当然也可以使用avdmanager命令下载系统镜像,读者自行研究哈。官方教程请戳这里

调试安卓系统

本来这里想通过查看虚拟机启动全部日志,可以查看系统开机过程,包括kernel等底层逻辑的,但是尝试了一下没有能够搞定,只能用adb来连接,那就不多介绍了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容