I've been playing around with Android development on my Ubuntu 14.04 machine. So far, the biggest problem I’ve encountered is manually testing my various applications - I only have a Nexus 4 and 7. It’s a big world out there, so testing on only 2 platforms probably isn't going to cut it for a real world app. Having already thought of this, Google provides an emulator as part of their SDK, with pre-defined hardware profiles for popular devices such as the Galaxy Nexus, Nexus 5, etc. They also provide a way to specify custom device profiles, so that you can test across nearly any combination of hardware and software.
The big problem with using the emulator is speed. Without hardware acceleration, the emulator is very slow. For example, spinning up a Nexus 4 image took upwards of 2 minutes. Once it was up and running, actually using the UI was a far cry from native device speeds. While this is acceptable if you’re only using the emulator to test once in a while, it can get frustrating if you want to use it as a primary testing device. It can also get frustrating if you want to perform unit tests on your application, since you need a device up and running to gain access to some compiled libraries (note that libraries such as Robolectric can really help you out!)
Luckily, Google is already on top of the speed issue. The emulator comes armed with options to accelerate the virtual machine. All you need is a platform that supports it. For those running genuine Intel processors, your computer must support the HAXM processor instruction set. For those running AMD processors, your computer must support the AMD-V extensions, and you must be running some flavour of Linux. Given that I have an AMD Phenom II hex core-processor running Ubuntu, I checked for the AMV-V extensions with the following:
cat /proc/cpuinfo | grep svm
The cat command dumps out the current CPU info stored in
/proc/cpuinfo. Next, we pipe that to a
grep command, looking for the
svm flag (SVM is the name of the AMD-V extension). Given that I have 6 cores, I get the following output repeated 6 times:
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm 3dnowext 3dnow constant_tsc rep_good nopl nonstop_tsc extd_apicid aperfmperf pni monitor cx16 popcnt lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt cpb hw_pstate npt lbrv svm_lock nrip_save pausefilter
A closer inspection shows
svm as one of the processor flags. So I know my computer can support VM acceleration, but according to the emulator VM acceleration documentation there are a few other tasks that must be completed before you can use the accelerated emulator. Briefly, the steps are:
- Install KVM.
- Download the Intel Atom x86 image.
- Configure an Intel based emulated device.
- Configure the IDE to start the emulator with acceleration.
Step 1: Installing KVM
KVM is a virtualization technology. The Ubuntu documentation has a nice description of KVM, and how to install it. I’ll provide some of the highlights here. First, I checked to see if my installation supported KVM by doing the following:
sudo apt-get install cpu-checker kvm-ok
I got the following output:
INFO: /dev/kvm exists KVM acceleration can be used
This is good news. Next up, I needed to actually install the KVM packages. I did this with:
sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
Following that, I ran a check to see if KVM was configured correctly:
virsh -c qemu:///system list
I got the following output:
error: failed to connect to the hypervisor error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': Permission denied
According to the documentation, the problem was due to permissions. My user needs access to the socket file
/var/run/libvirt/libvirt-sock. The file should be owned by
root, and have a group of
libvirtd. I also needed to add myself to the
libvirtd group. I did this with:
sudo useradd -G libvirtd thomas sudo chown root:libvirtd /var/run/libvirt/libvirt-sock
I then quickly logged out and back in again, and checked to ensure I was in the
libvirtd group. I re-ran the KVM check:
virsh -c qemu:///system list
This time I got the following output:
Id Name State ----------------------------------------------------
This meant I was good to go.
Step 2: Download the Intel Atom x86 Image
According to the Android development site, the ARM versions of the emulator cannot be accelerated. Instead, you must download and use the Intel Atom x86 image. Using Android Studio, this was as easy as opening up the Android SDK Manager, finding the Atom image, and installing it.
I also needed to install the Google APIs (x86 System Image) using the SDK Manager as well.
Step 3: Configuring an Intel-based Emulated Device
The next step was to configure a new device using the Intel Atom x86 image. This was as simple as creating a new device, and selecting Intel Atom (x86) as the CPU/ABI. To make it clear that this was an atom device, I named it with x86 tacked on the end.
Step 4: Configuring the IDE to start the emulator with acceleration
In this last step, I modified my run configuration on Android Studio to include these necessary switches to enable KVM:
-qemu -m 512 -enable-kvm
By clicking on Run, Edit Configurations... and then clicking on the Emulator tab, you can enter additional command line options that are passed to the emulator:
If you're like me, and you want to poke around with the image as it’s running, you can use QEMU's monitor to inspect various aspects of the running emulator. From the command line:
emulator -avd nexus_4_x86 -qemu -m 512 -enable-kvm -monitor stdio
When the emulator starts up, QEMU's monitor will prompt you for input as the emulator runs in the background. You can inspect many different aspects of the running emulator.
QEMU 0.10.50 monitor - type 'help' for more information (qemu) info kvm kvm support: enabled (qemu) info network VLAN 0 devices: user.0: ne2k_pci.0: model=ne2k_pci,macaddr=52:54:00:12:34:56 (qemu) info jit Translation buffer state: gen code size 0/133842944 TB count 0/1048576 TB avg target size 0 max=0 bytes TB avg host size 0 bytes (expansion ratio: 0.0) cross page TB count 0 (0%) direct jump count 0 (0%) (2 jumps=0 0%) Statistics: TB flush count 0 TB invalidate count 0 TLB flush count 276 [TCG profiler not compiled]
Booting the emulator (from the initial launch to the Android lock screen) using the ARM-based system image took 2 minutes and 45 seconds. Using the Intel Atom-based system image with acceleration took 30 seconds. This is roughly an 80% decrease in the amount of time needed to boot the emulator. While I don’t have exact time differences for common tasks in the Android UI, the accelerated emulator feels almost like a native device (in terms of speed).
While I still wouldn't want to rely on an emulator for running unit tests, the accelerated emulator does provide a nice way of performing manual testing. In a future post, I'll look at how to perform unit testing without having to rely on an emulated environment.