The Ultimate Guide to a Bulletproof Capacitor Android Setup on Mac

I have been working on converting a couple of React apps into mobile apps with Capacitor. Capacitor is an open-source framework that empowers web developers to build true native mobile apps for iOS and Android using a single codebase of HTML, CSS, and JavaScript. It provides a consistent, web-focused API that unlocks native device features without forcing you to leave the web ecosystem. You can learn more at the official Capacitor website.
This guide was born from an excruciating multi-day troubleshooting session filled with cryptic build errors and version conflicts. After finally navigating the maze, this document serves as the definitive, command-line-first approach to creating a stable and repeatable Android development environment on a Mac, so you can avoid the headaches and get back to building.
Step 1: Understanding the Android Project Structure
After running npx cap add android
, Capacitor creates a complete, native Android project in the android
folder. Here are the key files you’ll interact with:
android/build.gradle
: The top-level Gradle configuration file. We’ll edit this to set the Android Gradle Plugin (AGP) version.android/app/build.gradle
: The app-level configuration. We rarely need to touch this directly.android/variables.gradle
: A Capacitor-specific file for setting and managing versions for your SDK, Java, and other dependencies. This is one of our most important files.android/gradle/wrapper/gradle-wrapper.properties
: This file defines which version of the Gradle tool your project will use. The./gradlew
command reads this file to download the correct version, ensuring consistency.
Step 2: The Command-Line Toolkit (SDKMAN! & Android SDK)
To avoid version conflicts, we will manage our Java and Android SDKs manually from the command line, giving us full control.
A. Install Java with SDKMAN!
SDKMAN! is a command-line tool for managing multiple Software Development Kits, including Java. It’s the perfect way to install and switch between JDK versions without messing up your system.
- Install SDKMAN!:
curl -s "https://get.sdkman.io" | bash
Follow the on-screen instructions, which will ask you to run a command like source "$HOME/.sdkman/bin/sdkman-init.sh"
.
- Install and Set Java 21: Modern Capacitor versions require Java 21.
# Install a trusted distribution of Java 21 (Temurin is excellent)
sdk install java 21-tem
# Set it as the default for your system
sdk default java 21-tem
Verify your installation: Close and reopen your terminal, then run:
java -version
B. Install Android SDK Command-Line Tools
You need the Android SDK, but you don’t need to install the entire Android Studio IDE just yet.
- Download the “Command line tools only” package for macOS from the official Android Studio download page.
- Create a directory for your Android SDK. A common location is
~/Library/Android/sdk
. - Unzip the downloaded package. It will create a
cmdline-tools
directory. Move its contents into alatest
sub-folder, so the final path looks like this:~/Library/Android/sdk/cmdline-tools/latest/
- Configure your
.zshrc
file: This is the most critical step. Open~/.zshrc
(or~/.bash_profile
) and add the following environment variables. This tells your system where to find the Android tools.
# Add this to your ~/.zshrc file
# For SDKMAN! (it should have added this automatically)
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"
# For Android SDK
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
- Install the SDK Platforms: Close and reopen your terminal, then use the
sdkmanager
tool you just added to your path to install the necessary components. Modern Capacitor requires SDK 35.
# Install platform-tools (provides the essential 'adb' command)
sdkmanager "platform-tools"
# Install the SDK for Android 15 (API 35)
sdkmanager "platforms;android-35"
Step 3: Aligning Your Project’s Gradle Configuration
Now we’ll edit the files in our android
project to use the versions we just installed.
- Set the Android Gradle Plugin (AGP) Version:
- Open
android/build.gradle
. - Update the
classpath
to use a modern AGP version like8.5.0
.
// In android/build.gradle
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:8.5.0'
// ...
}
}
- Set the Gradle Wrapper Version:
- Open
android/gradle/wrapper/gradle-wrapper.properties
. - Update the
distributionUrl
to use a Gradle version compatible with your AGP, like8.7
.
# In gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
- Set Your SDK and Java Compatibility Versions:
- Open
android/variables.gradle
. - This is where you tell your project to use the SDK 35 you installed and to compile with Java 21 compatibility.
// In android/variables.gradle
ext {
minSdkVersion = 24
compileSdkVersion = 35 // Match the SDK you installed
targetSdkVersion = 35 // Match the SDK you installed
// ... other versions
sourceCompatibility = JavaVersion.VERSION_21 // Match Java 21
targetCompatibility = JavaVersion.VERSION_21 // Match Java 21
}
Step 4: Mastering the Gradle Wrapper (./gradlew
)
Capacitor commands like npx cap run
use Gradle under the hood. You’ll often see the ./gradlew
command, which refers to the Gradle Wrapper.
- What it is: The Gradle Wrapper is a script included in your
android
project that ensures every developer on your team builds with the exact same Gradle version. It reads thedistributionUrl
in yourgradle-wrapper.properties
file. - What it does: When you run a
./gradlew
command, it checks if the specified Gradle version is downloaded. If not, it downloads it automatically and then executes your command. This creates reproducible builds and avoids “it works on my machine” problems.
A crucial command for troubleshooting is clean
.
- Cleaning the Build Cache:
# Navigate into the android directory first
cd android
# Run the clean task
./gradlew clean
The ./gradlew clean
command deletes the android/app/build
directory, which contains all previously compiled code and cached files. If your build is behaving strangely after a configuration change, running a clean is the best first step to ensure you’re starting fresh.
Step 5: The Capacitor Workflow: sync
vs. run
With the setup complete, your daily workflow will use two main commands. It’s vital to understand the difference.
npx cap sync android
: This command only copies your web assets (HTML, JS, CSS) into the native Android project. It does not build or install the app. Think of it as preparing the ingredients.npx cap run android
: This is the all-in-one command. It automatically performs async
, then builds the.apk
file, and finally installs and launches it on your connected Android device or emulator. This is the command you’ll use 99% of the time for testing. 🚀
Step 6: Testing on Your Device (Without Android Studio)
- Enable Developer Options on your Android device (go to Settings > About Phone and tap on the “Build Number” 7 times).
- Enable USB Debugging inside the new Developer Options menu.
- Connect your device to your Mac with a USB cable. Approve the “Allow USB debugging?” prompt on your device.
- Verify the connection in your terminal. You should see your device listed.
adb devices
- Run your app!
npx cap run android
Your app will build and magically appear on your device screen.
Step 7: How to Safely Use Android Studio
Even with a command-line setup, Android Studio is an invaluable tool for debugging, viewing logs (logcat
), and analyzing your app. Here’s how to use it without letting it break your careful setup.
- Install Android Studio: Download it from the official website and run through the standard installation. It will detect and use your existing
ANDROID_HOME
SDK location. - Open Your Project: Use “Open” and select the
android
folder inside your Capacitor project. - ⚠️ The Critical Step: Android Studio will analyze your project and may offer to “Upgrade Android Gradle Plugin” or perform other “sync” actions. Be careful! You have already configured your versions perfectly. It’s best to ignore these automatic upgrade prompts for now.
- Check the Gradle JDK: This is the #1 reason for conflicts between the command line and the IDE. Go to Android Studio > Settings > Build, Execution, Deployment > Build Tools > Gradle. Make sure the Gradle JDK is set to the same Java 21 version that SDKMAN is using. If it’s not, select it from the dropdown. This ensures the IDE and your terminal are using the exact same Java version to build your project.
This is it! These set of instructions are what unblocked me on the project I’m currently working on. Once I’m done I’m sure I’ll revisit these to improve my workflow. I hope this guide will save you a lot of headaches. Cheers! 😎