Background/Environment
Virtuality
I like to develop inside a Virtual Machine. The advantages of this are:
- The environment is portable, so I can move it between machines;
- I can backup and/or snapshot machines so can recover quickly in the event of some failure or error;
- I can set up different machines for different workflows (e.g. one for API dev, one for Mobile dev etc.)
- I can replace my physical device without going through the pain of rebuilding my development environment, and;
- I can use any OS on my physical device
I use VirtualBox to host my guest machines – I’ve tried Microsoft Hyper-V, but for whatever reason it just lacks the performance and flexibility that I’ve found with VirtualBox.
Android and Virtual Machines
Unfortunately Android emulators don’t work all that well inside a virtual guest. Despite reading good things about Hyper-V allowing nested hypervisors, Hyper-V inside Hyper-V just doesn’t perform well enough to be useful. VirtualBox 6.1 is supposed to support nested hypervisors, but I don’t think it’s quite there yet.
So, for Android testing and debugging, I appear to be stuck with either using a physical device (which was quite easy to get up and running from my VM), or, run the emulator on my physical machine, outside my VM.
The downside of using a physical device, is that my app needs to talk to an API that is hosted on a test environment with a non-public DNS name. So, without rooting my phone, I can’t run and debug the app.
The End Goal
So, my ultimate and preferred strategy/environment is:
- Windows 10 VirtualBox guest machine running Visual Studio with Xamarin;
- Android emulator running on my physical device through Android studio;
- API and backend (MS-SQL) database running on a separate VM or physical server;
- Emulator has custom hosts file to allow DNS access to API server;
You’d think that would be a simple ask, and I wouldn’t be an orphan in expecting to operate in that environment……..
The solution
On the physical machine install Android Studio environment
- Install Android Studio, Android SDK and tools etc. I use Chocolatey so it was easy enough to get it installed that way;
- I moved my SDK files and environment from the default location to D:\android – Studio and the SDK’s and machine images take up quite a bit of disc space that I didn’t have on my C: drive
- In the SDK manager, under SDK Tools, install the Emulator Accelerator (HAXM) [Note: It’s not possible to use the Hyper-V emulator for the reasons given above].
- In AVD Manager, create an appropriate virtual device (emulator). Using an API emulator rather than one based on Google Play is better for this;
Add custom hosts file to Android emulator
The simplest way to get the emulator talking to my API host is to copy a custom hosts file onto the emulator system
- Open a Powershell window and run the created emulator in Writable mode:
D:\Android\emulator\emulator.exe -writable-system -avd [your created emulator name here] - Open another Powershell window and find out the emulator name for your emulated device:
adb devices
(For whatever reason, I end up with two emulators when I query adb devices, so must specify the -s argument each time) - Now execute:
adb -s emulator-5554 root
adb -s emulator-5554 shell avbctl disable-verification <— this will not freeze the emulator
adb s emulator-5554 reboot - Wait for the emulator to restart, once restarted, execute:
adb -s emulator-5554 root
adb -s emulator-5554 remount - Modify a hosts file to add the appropriate DNS values and, from the directory the file is saved in, execute:
adb -s emulator-5554 push hosts /system/etc/hosts
adb -s emulator-5554 unroot - Shut down the emulator, which should save a snapshot of the current state. This will ensure you shouldn’t have to repeat the above steps for this emulator each time you want to run a debugging session
Configure the physical machine to run the emulator in TCPIP mode
Debugging from the VM to the physical machine is done over TCPIP. By default the emulator uses USB, which doesn’t seem to work with the Visual Studio/Android debugger (at least I couldn’t get it to). To turn on TCPIP connections on the emulator execute the following in PowerShell:
adb -s emulator-5554 connect 127.0.0.1:5555
Running a debugging session
Start the emulator on the physical machine
- From now on, run the emulator in writable mode in order to debug it. If you run it without the -writable-system flag, the modified hosts file is not available and the system runs with the standard, default hosts file:
D:\Android\emulator\emulator.exe -writable-system -avd Nexus_6P_API_29 - Ensure that the ADB service is not running on the physical machine after loading the emulator by:
adb kill-server
Set up port forwarding to SSH from the virtual machine to the physical device
The debugger uses SSH to create a connection between the machines
- ssh -NL 5556:localhost:5554 -L 5557:localhost:5555 [username]@[physical-machine-name]
- In PowerShell on the VM, run:
adb-devices
and find out the name of the remote emulator, it should match the port used in the SSH command, in my case it’s emulator-5556 - Configure the VM emulator to connect via TCPIP
adb -s emulator-5556 tcpip 5557 - Connect emulator to local loopback port (which is then redirected via SSH to remote emulator)
adb -s emulator-5556 connect 127.0.0.1:5557
After all that, you should now see the remote device in the list of devices available to the Visual Studio, Android debugger in your Xamarin project: