Where Android Emulator fits in a robust Capacitor development loop
In Capacitor development, web code runs inside a native mobile shell. I use a web preview, a virtual device, and a physical phone to get the right kind of feedback for each change.
| Layer | Use it for | Main blind spot |
|---|---|---|
| Web app | Fast UI and shared-code feedback | Native shell, WebView, plugins, and mobile lifecycle |
| Android Emulator or iOS Simulator | Routine mobile integration, packaging, lifecycle, and repeatable development checks | Real hardware, performance, OEM software, and some store behavior |
| Physical phone | Real-device checks for hardware, performance, connectivity, default apps, and release context | Slower to reset, harder to automate, and limited to the devices you own |
For me, virtual devices are often the sweet spot: they exercise the mobile shell and integrations while remaining faster to reset and automate than physical phones.
How the middle layer earned its place
At one company, we first introduced Android emulators and Apple simulators. Then we found problems they did not expose, so the company provided physical phones. Those phones helped us find more bugs and corner cases. We kept both virtual and physical devices. For routine build-and-run work, starting a virtual device that was already configured was quicker than connecting and preparing one of those phones. It was also easier to reset or discard.
Physical phones also have a setup cost. Taking several of them away from the desk means carrying cables, chargers, and sometimes adapters. For projects where phones need to reach a development environment running on my laptop over Wi-Fi, I also have to create a temporary network and configure each phone. A virtual device is already on the laptop and can use the host connection. Away from the desk, it is simply easier to start.
My opinion about Android Virtual Devices also changed over time. I used Genymotion personally and initially treated AVD as the less convenient option. Companies I worked with usually did not see enough additional value in adopting another emulator. After learning how much of AVD could be controlled from the command line, I found that a properly configured AVD gave me more control over setup, recovery, and integration than I had expected. AVD became my preferred option.
Side note: Apple’s Simulator and Android Emulator are implemented differently. Apple describes Simulator as running simulator builds directly on the Mac. Android uses a virtual-machine model that can run through a hypervisor. In everyday development, both provide a fast, controllable environment for running a mobile build before checking real hardware.
AVDs are commonly started from Android Studio’s Device Manager. I prefer running Android Emulator as a standalone tool. The Android and iOS workflows then feel coherent: start a virtual device, build the app, install it, and inspect the result without making either IDE the center of the process.
Choose the Android image by capability
An Android Virtual Device (AVD) boots from a system image: the packaged Android operating system for that virtual device. The available images differ in the capabilities and restrictions they include.
Google APIs images include access to Google Play services. Play Store images also include the Play Store and are release-signed, which means they do not allow the elevated root access used by some development workflows.
That distinction mattered in one Capacitor setup. The app needed to
reach a development service on the host while preserving its expected HTTPS
hostname and WebView origin. Inside the AVD,
10.0.2.2 is a special alias
for the development host’s loopback interface. The setup mapped a development
hostname to that address. Doing so required a writable system partition, root
access, and remounting that partition.
The Google APIs image let me run adb root and adb remount, then change the
hosts file. The Play Store image did not. I therefore choose between them based
on the system access the workflow needs.
For a long time, I avoided Play Store images because I believed they were slower. On my machine, the Play Store image’s median cold boot took 20 seconds, compared with 22 seconds for Google APIs. The sample was small and host-dependent, so I do not treat it as a general speed ranking.
A Google APIs image is my daily default: it provides access to Play services with enough system control for this scripted setup. For broader release confidence, I switch to a physical phone, which adds real hardware, performance, OEM software, and default apps. I keep Play Store images for testing release-signed restrictions and creating a clean, disposable store environment.
Four tools cover the AVD lifecycle
The CLI became much easier once I gave each tool one job:
sdkmanagerinstalls SDK packages.avdmanagercreates and deletes device definitions.emulatorlists and boots those devices.adbdrives a running device: install, launch, inspect logs, move files, and change settings or emulated conditions.
It is a useful exercise to run these commands manually and explore their capabilities before wrapping them in scripts.
A developer can type these commands directly. The same commands also work in scripts and agent workflows.
A team script can choose an AVD, build, install, launch, collect logs, and surface the failures the team has chosen to treat as blocking. I recently wrapped our setup, build-and-run, and live-reload flows into agent skills. The team now uses these skills and AVD scripts daily to speed up development and testing.
Because provisioning and daily operation use separate tools, a stale setup can
stay hidden for a long time. My existing AVD kept running through emulator
and adb, while creating a new one failed because sdkmanager and
avdmanager had not been updated with the rest of the toolchain. Reinstalling
current Command-line Tools did not fix the problem until I updated PATH,
which still pointed to obsolete binaries.
Before provisioning, check which binaries will run:
command -v sdkmanager avdmanager emulator adb
sdkmanager --version
emulator -accel-check
emulator -list-avds
adb devices
Android documentation
recommends ANDROID_HOME and marks ANDROID_SDK_ROOT as deprecated. Older
manuals, and AI answers based on them, may still use the old name.
For a quick manual run, the daily loop is short. Choose an unused even-numbered
emulator console port;
Android Emulator pairs it with the next port for ADB. Using that same console
port in SERIAL keeps every command pointed at the same emulator:
PORT=5554
SERIAL="emulator-$PORT"
emulator -avd Medium_Phone_API_36 -port "$PORT" &
adb -s "$SERIAL" wait-for-device
adb -s "$SERIAL" shell 'until [ "$(getprop sys.boot_completed)" = "1" ]; do sleep 1; done'
adb -s "$SERIAL" install -r ./app-release.apk
Boot, connect, wait for Android, then install. adb wait-for-device only waits
for the ADB connection, so the next line waits for Android itself. Team
automation should also add a boot timeout and cleanup.
Recover only as much state as necessary
An AVD is useful partly because its state has several recovery levels. Android’s snapshot model usually lets me try a smaller reset before deleting the device. When an AVD acts strangely, I move through these reset options:
| Level | Use it when | State boundary |
|---|---|---|
| Quick Boot | You want the fastest startup and trust the saved state | Restores the saved AVD state, including apps and settings |
Cold boot with -no-snapshot-load |
My usual choice when I want to avoid snapshot surprises | Starts the OS without loading a snapshot but keeps writable user data |
Start with -wipe-data |
You need clean app data, settings, or a first-install check | Recreates user data and removes installed apps and settings, but not an emulated SD card |
| Delete and recreate the AVD | The definition itself is suspect or nothing from it should remain | Removes the AVD definition, after which provisioning must create it again |
Installed apps and files in the AVD’s user data persist through Quick Boot and cold boot. Wipe data and recreating the AVD remove them.
The recorded startup times were 7 seconds for a confirmed Quick Boot, 22 seconds for a cold boot, and 44 seconds for the initial boot. These are the times from my machine during a regular workload. For me, 22 seconds is cheap enough that I generally prefer a cold boot, while the longer initial boot keeps recreation as the final recovery step.
If an image feels slow, start with emulator -accel-check. Host and guest
architecture, RAM pressure, disk space, graphics mode, and snapshot state can
all affect performance.
Know when virtual is not enough
Virtual devices provide a controlled, repeatable environment. They do not include every part of a user’s phone.
On one Samsung phone, an important web flow opened in the user’s default Samsung Internet browser rather than the Chrome path we had tested. Samsung Internet is Chromium-based, but it has its own cookie, privacy, and tracking controls. Those settings can change how the same site behaves. Our generic AVD with a controlled browser did not include that default-app environment.
Physical phones also add real performance, memory pressure, connectivity, optional hardware, manufacturer services, and preinstalled apps. After more than ten years in mobile development, I still prefer adding one constrained physical phone when the goal is to expose more real-world failures.
Among Android developers I knew, the LG K9 became a memorable example. We joked that this low-end phone was a legendary bug finder because it exposed problems that more capable phones missed.
My rule is to choose the weakest phone the product actually supports. Testing an obsolete phone outside that range tells me little about the failures current users will see.
Production monitoring can refine the choice. If an issue clusters on one exact model, OS, firmware, or WebView combination, that is a stronger reason to acquire or reserve the device than a general belief that obscure manufacturers cause more bugs. Platform-only stack frames and one-off Application Not Responding (ANR) events give me only a place to start investigating.
Use device matrices selectively
It is worth remembering that you can launch more than one emulator at once. I can use multiple local AVDs or a hosted device matrix to run the same check across several configurations. Options include:
- Build-managed devices let Gradle create, reset, and tear down virtual devices for automated instrumented tests on local or CI machines.
- Firebase Test Lab runs tests across hosted virtual and physical devices. Its usage-based pricing can quickly make broad physical matrices expensive.
- AWS Device Farm and BrowserStack App Automate are alternatives when hosted real-device coverage is the main need.
I have mostly seen teams use these services for selected checks, if at all. Local and manual checks remain the daily default.
Agents reuse the same device controls
The CLI scriptability described earlier also supports agents. The device work does not change: start a virtual device, wait for Android, install the app, and collect a screenshot, log, or test result. An agent needs a way to call those steps and inspect what they return.
Some current tools wrap this lower-level device control in an interface built
for agents. Apple’s Device Hub
puts simulated and physical devices in one place. Apple’s
coding-agent workflow
can operate the simulated device and return screenshots and test summaries,
while devicectl provides a command-line route for scripts and CI.
Claude Code Desktop can place an interactive iOS Simulator pane beside the conversation. Other agent workflows can connect through lower-level tools: OpenAI documents an iOS Simulator workflow through XcodeBuildMCP.
These examples use iOS, but the relationship is the same as in the Android workflow described above: device commands perform the work, while the agent integration decides when to run them and returns the result.
Android’s experimental AppFunctions API lets developers expose selected app actions as tools that authorized callers can discover and execute. This sits at a different layer: the caller invokes an action declared by the app instead of driving each step through the device UI. It covers only the actions the app declares, and full system-agent access remains limited during the experimental rollout.
For UI-driven Android checks, an agent still needs an emulator it can start,
reset, control, and inspect. That work continues to use adb, Emulator CLI,
ordinary tests, and web previews.
An agent does not gain capabilities beyond the tools it can call. It remains bounded by the emulator’s capabilities and blind spots. Checks involving hardware, performance limits, OEM software, default apps, or real connectivity still need physical phones.
What this looks like in daily work
I stay in the web preview while a change touches only the UI or shared web logic. When it reaches the Android shell, WebView, plugins, or lifecycle, I start an AVD with a Google APIs image from one of my script templates. I use a Play Store image only when I need the store itself or its release-signed restrictions.
An Android phone remains beside my laptop for hardware, performance, OEM software, default apps, real connectivity, and release checks. The AVD covers the repeatable daily loop. The phone covers behavior I cannot reproduce there.