Suckless Android SDK setup

Saturday, 27 May 2023 / last updated: Sunday, 26 May 2024

Android SDK is one of the first things I set up on a new computer. This post is a reference for future me and for anyone who wants to do it right and learn something along the way.

This post is for you if you:

Preface and rant

Why am I writing this post? Surely there are official guides on installing Android SDK?

Well, I think that the official, recommended way of downloading Android SDK sucks. More specifically, telling everyone to install Android Studio sucks.

I’m under the impression that the official docs are optimized for the lowest common denominator type of person. That’s not a bad thing when you’re just getting started with Android development, but after doing the setup of SDK a few times, I want to know more about what I am doing and why I am doing it. And that’s what the official docs fall short of.

Another problems appears when building on CI. The more popular ones have some kind of pre-built “Set up Android SDK” step, but it’s not always the case. And you won’t install Android Studio on a CI server, right. Right?

I also strongly believe that everyone should know a thing or two about the tools they’re depending on every day.

Basics

Install Java Development Kit

On macOS, I prefer to use the OpenJDK distribution provided by Homebrew, but other ones (like Eclipse Temurin) are also fine:

brew install openjdk

Unless you have valid reasons not to, you should the latest JDK.

Here’s a great post from Jake Wharton that explains this in much more detail. Go read it!

Then, set JAVA_HOME and add the binaries to PATH. To do it, open your ~/.bashrc, ~/.zshrc, or whateverrc you use and add:

export JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk.jdk/Contents/Home"
export PATH="$JAVA_HOME/bin:$PATH"

Install core command-line tools

The official, recommended way to get Android SDK is to simply download Android Studio from developer.android.com/studio. (You see? There’s no /download, there’s /studio! They’re trying so hard to shove Android Studio down our throats! /s). I don’t like installing Android Studio because it’s heavy and because I already have IntelliJ IDEA installed. Why should I bother installing something I won’t use?

So on that page, instead of clicking “Download Android Studio”, scroll down and find the “Command line tools only” section and then download the variant for your OS. Alternatively, you can use curl:

$ curl --remote-name https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip

To make things simpler, let’s assume we’re in the Downloads directory:

$ cd ~/Downloads

Now extract the downloaded archive:

$ unzip commandlinetools-*.zip

Extracting the zip created the cmdline-tools directory. Let’s see what executable binaries it contains:

$ ls -1 cmdline-tools/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

The program that is most interesting to us now is sdkmanager, which allows for downloading additional Android SDK components. But before doing that, let’s move the cmdline-tools directory into the right place.

Let’s put this extracted directory into ~/Downloads, so that we’ll be able to find it at ~/Downloads/cmdline-tools.

First, create a place where Android SDK will be located. In my case, it’s always ~/androidsdk, because that’s where I like having it.

$ mkdir ~/androidsdk

Then create a directory for the command-line tools:

$ mkdir -p ~/androidsdk/cmdline-tools/latest

Now we’re ready to copy the contents of the cmdline-tools directory into the final location:

$ cp -r ~/Downloads/cmdline-tools/* ~/androidsdk/cmdline-tools/latest

The reason why I’m using latest is that you might want to install other versions of the command line tools. Google recommends doing it this way.

Modify PATH

Finally, you want to add the path where command-line tools live to PATH, and also export 2 environment variables. Open ~/.zshrc and add these lines:

export ANDROID_HOME="$HOME/androidsdk" # sdk lives here
export ANDROID_USER_HOME="$HOME/.android" # config and tmp files live here
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"

ANDROID_HOME and ANDROID_USER_HOME variables are used by other tools, such as Android Studio and flutter CLI. When they’re not defined, those tools often try to guess other common locations. I think it’s a good practice to define them explicitly.

Reload the shell to apply these changes. For example, if you happen to be using zsh, run exec zsh.

To learn more about the environment variables used by Android SDK, see this page.

More advanced stuff

At this point, you’ve got the basics done. You should be able to go to your app project and run ./gradlew :app:assembleDebug, or flutter build apk, or similar, and it should just work. If you’re in hurry, you can stop reading here!


The first build will take quite a long time, and if you observe the logs carefully, you’ll notice that Gradle downloads a bunch of stuff. In the rest of this post, I’ll take a closer look at what exactly is being downloaded, and what’s the purpose of each component.

Install more tools with sdkmanager

Now you’ve got yourself a few command-line tools. But that’s about it. You still don’t have any build tools (compilers, resource mergers, shrinkers, that sort of stuff), system images, and even an emulator.

To get them, you use sdkmanager. It’s part of the cmdline-tools zip we’ve just downloaded and it lets you install everything you might need in your Android development journey. It’s located in ~/androidsdk/cmdline-tools/latest/bin. BTW, from now on, I’ll use $ANDROID_HOME instead of ~/androidsdk now that it’s set.

Now let’s run sdkmanager again and download stuff that is always needed.

A useful command to remember sdkmanager --list_installed. Much faster than opening up the GUI of SDK manager in Android Studio.

Emulator

It’s not necessary to build apps, but most people use it. Let’s install it:

$ sdkmanager --install 'emulator'

This will install a bunch of stuff, including the emulator binary, under $ANDROID_HOME/emulator. Let’s add that directory to PATH so we’ll be able to run emulator from anywhere. Open .zshrc and append:

export PATH="$ANDROID_HOME/emulator:$PATH"

System images

System images are only needed if you plan to use the emulator. You’ll save a few gigs of disk space by not downloading them.

I’m on MacBook powered by Apple Silicon, so I download arm64-v8 variants:

$ sdkmanager --install 'system-images;android-33;google_apis;arm64-v8'

If you’re on a more classic PC box, you’ll likely want to replace arm64-v8 with x86_64.

And to download the same system image, but with Play Store and a few more Google apps installed:

$ sdkmanager --install 'system-images;android-33;google_apis_playstore;arm64-v8'

Build tools

To build apps, you need build tools. By default, Android Gradle Plugin (AGP) takes care of downloading the right version of build tools, so you usually don’t have to care about them, but if you’re curious, read on.

As everything else in Android SDK, build tools are installed using sdkmanager (That’s what AGP does under the hood as well):

$ sdkmanager --install 'build-tools;33.0.2'

After the installation completes, this particular build tools version can be found under $ANDROID_HOME/build-tools/33.0.2. Some of the most important executables in that directory are:

There are many build tools, but each one of them does one thing and does it well. They’re invoked by the build system (usually it’s Android Gradle Plugin who calls them) and few people use them directly (unless they’re something less common, such as integrating with an alternative build system). If you’d like to learn more about the build tools, head over here.

Adding build tools binaries to PATH is a bit more involved, because there are often many versions of them under $ANDROID_HOME/build-tools. Here’s what I have in my ~/.zshrc to automatically export the latest version:

if [ -d "$ANDROID_HOME/build-tools" ]; then
    build_tools=$(
        command ls "$ANDROID_HOME/build-tools" |
            sort --version-sort --reverse |
            head -n 1
    )

    export PATH="$ANDROID_HOME/build-tools/$build_tools:$PATH"
fi

Platform tools

(AGP handles these automatically, but we’re not interested in that right now)

Platform tools let you interact with a running Android device, either physical or virtual.

$ sdkmanager --install 'platform-tools'

After the installation completes, platform tools can be found under $ANDROID_HOME/platform-tools. Again, let’s add that directory to PATH:

export PATH="$ANDROID_HOME/platform-tools:$PATH"

The most famous binary inside platform-tools is definitely adb. If you’ve been into custom ROMs at some point, you might’ve also heard of fastboot.

Platforms

(AGP handles these automatically, but we’re not interested in that right now)

First, let’s get installation out of our way:

$ sdkmanager --install 'platforms;android-33'

Unsurprisingly, platform android-33 will be installed under $ANDROID_HOME/platforms.

But what are platforms?

A “platform” includes the source code of classes that are part of the OS, so that IDEs can show the code when you navigate to a symbol from the android namespace, for example Context or Bundle.

The packages from the android top-level namespace in the beginning aren’t built into the APK. They’re only placed on the compile classpath, but that’s it. The real implementation is provided by the OS itself. To illustrate this, let’s consider this Java file:

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
  // ...
}

The packages starting with android are implemented in the OS and made available to your app at runtime. On the other hand, the androidx packages are “extra” and they are bundled into the APK. It’s easy to verify that yourself by running apkanalyzer (from cmdline-tools) on the APK.

./gradlew :app:assembleDebug
cd app/build/outputs/apk/debug
apkanalyzer dex packages app-debug.apk --defined-only | grep '^C' # only classes

There’ll be lots of classes whose namespace starts with androidx namespace, but (almost) none of them will be from the android namespace.

Summary

And that’s it for this article. Before we part, here’s a shell script for me in the future to quickly install what I need:

sdkmanager --install 'emulator'
sdkmanager --install 'sources;android-34'
sdkmanager --install 'system-images;android-33;google_apis;arm64-v8a'
sdkmanager --install 'system-images;android-33;google_apis_playstore;arm64-v8a'
sdkmanager --install 'system-images;android-34;google_apis;arm64-v8a'
sdkmanager --install 'system-images;android-34;google_apis_playstore;arm64-v8a'
sdkmanager --install 'platform-tools'
sdkmanager --install 'platforms;android-34'

Last thing: here’s my own shell config file, from where you can easily copy the PATH-related commands. Be inspired by it! (or just copy-paste)

Now you’ve got everything set up, and hopefully you’ve also learned something about the SDK’s structure. You can go to your Android app project and start building it.


2024-08-18 – frp (Fast Reverse Proxy) instead of ngrok
2024-08-16 – Cirrus CI is the best CI system out there
2024-08-14 – Going to Berlin for Droidcon/Fluttercon
2024-06-25 – I was awarded Google Open Source Peer Bonus
2024-06-04 – My journey to Google I/O ’24
2024-05-11 – GitHub Actions beg for a supply chain attack
2024-03-19 – Writing a custom Dart VM service extension (part 1)
2024-02-08 – On using smartphone for things that make sense
2023-11-30 – Semantics in Flutter - under the hood
2023-11-25 – Flutter Engine notes
2023-09-17 – Creating and managing Android Virtual Devices using the terminal
2023-05-27 – Suckless Android SDK setup
2023-05-26 – Let’s start over
2023-05-21 – Short thought on “The Zen of Unix”
2023-05-15 – Notes about “flutter assemble”
2019-01-07 – Google Code-in 2018