Sparehand

Docs › Android emulators on the workers

Android emulators on the workers

An Android emulator takes 4 to 10 GB of memory while it runs. Several of them at once (one per coding session, say) fill even a big Mac. sparehand emulator boots them on your other machines instead. They run headless there, and adb on this computer reaches each one at a serial of its own:

sparehand emulator setup myworker            # once per machine: the SDK, the emulator, a system image
serial=$(sparehand emulator start Pixel_9)   # boots it on a free worker, prints 127.0.0.1:15555
export ANDROID_SERIAL=$serial                # adb, Gradle and test runners use it from here on
adb install app.apk && adb shell am start -n com.example/.MainActivity
adb exec-out screencap -p > screen.png
sparehand emulator stop $serial

start exits 255 when no worker can take the emulator, the same rule as sparehand run. Boot one on this computer then. A script or an agent does it like this:

if serial=$(sparehand emulator start Pixel_9); then export ANDROID_SERIAL=$serial
else emulator -avd Pixel_9 -no-window & fi   # (exit 255: nothing free; boot it here)

Commands

CommandWhat it does
sparehand emulator setup WORKER [--api N]Installs what is missing on that machine, in the user's home and without sudo: a JDK (Eclipse Temurin 21, only when there is no Java 17 or newer), the Android command-line tools, platform-tools, the emulator and a system image for API N (default: the newest stable one). The image is google_apis for the machine's own architecture: x86_64 on Linux and Windows (WSL), arm64-v8a on a Mac with Apple silicon. It also accepts the licences. It says what an administrator still has to do, if anything (see below). Run it again after that.
sparehand emulator start [AVD] [--on WORKER] [--ram 3G] [--api N]Boots an emulator and prints its serial (127.0.0.1:PORT) on stdout; progress goes to stderr. With the name of one of this computer's AVDs (~/.android/avd), the worker gets an AVD of the same name, Android version, image kind (google_apis, google_apis_ps16k…) and device, in its own architecture. The image is downloaded the first time it's needed. Without a name: sparehand_37 for --api 37, sparehand_latest otherwise. --ram: its memory, 1G to 16G (default 3G, whatever the AVD says). --on: only that worker.
sparehand emulator test [AVD] [--on WORKER] [--ram 3G] [--api N] [-- COMMAND…]An instrumented test run in one command: boots an emulator like start, runs COMMAND (default ./gradlew connectedDebugAndroidTest) on that same worker against it, brings the reports back and stops the emulator. See mobile builds.
sparehand emulator listThe running emulators: serial, machine, AVD, memory, how long it has run, and who started it (the checkout it was started from and by what, e.g. my-app (Claude Code); SPAREHAND_EMULATOR_BY sets it). --tsv is for scripts.
sparehand emulator stop SERIAL|allShuts it down on the worker (adb emu kill, then its process group), removes the forward and runs adb disconnect here. all stops every emulator you started.

The dashboard shows the running emulators on each machine's card, with a Stop button for owners. The Activity tab lists every start, stop and setup.

Which machine gets it

Only machines that emulator setup has made ready are used, in this order:

  1. A machine at home before a cloud one. A Linux or Windows (WSL) machine before a Mac. Then the one with the most memory free.
  2. A machine must have room the way it would for a job: on, not paused, not strained, a job slot free, SPAREHAND_MIN_MEM free, CPU and GPU not busy. It also needs the emulator's memory plus 2 GB free.
  3. A Mac takes at most SPAREHAND_EMULATOR_MAC_MAX emulators (1) at a time.
  4. A stopped cloud worker (sparehand cloud add) is started for it only when nothing at home can take it, and never past its caps.

A running emulator counts as a job on its machine. It holds a job slot, its memory counts, and sparehand status lists it. A job that arrives later is the one stopped when the machine runs out of memory, not the emulator.

When it stops by itself

An emulator that stopped by itself loses its forward and its adb connection here the next time Sparehand looks at that machine (status, list, the dashboard).

How it works

What an administrator may need to do

emulator setup says so, with the exact command:

Settings

SettingDefaultMeaning
SPAREHAND_EMULATOR_IDLE_MIN30Minutes without adb traffic after which an emulator stops (0: never).
SPAREHAND_EMULATOR_MAX_HOURS8Hours an emulator may run in all (0: no limit; a cloud worker's daily cap still applies).
SPAREHAND_EMULATOR_RAM3GIts memory when --ram isn't given.
SPAREHAND_EMULATOR_PORT15555The first local port for the serials.
SPAREHAND_EMULATOR_MAC_MAX1Emulators at once on a Mac worker.
SPAREHAND_EMULATOR_BOOT_WAIT420Seconds a worker waits for sys.boot_completed (set on the worker, in ~/.sparehand/env).

For a coding agent, one line in its instructions does it: "Android emulators go through sparehand emulator start; use the printed serial as ANDROID_SERIAL; sparehand emulator stop when done; boot a local one only on exit 255."