Reading the SDK
An API level is a boundary in a very large surface. This page teaches the four primary sources — the reference, the API diff, the platform source and the behaviour notes — and how to read each one without being misled by what it leaves out.
The Android SDK is one of the largest public APIs ever shipped, and it changes every year. "Read the SDK" is not a metaphor: there are documents that tell you exactly what changed, and they are generated from the SDK itself. The skill is knowing which document answers which question, and where each one is allowed to lie to you.
TL;DR#
- The
android.jaryou compile against is the contract. Reference docs describe it; the API diff enumerates its changes; neither is more authoritative than the stub itself. compileSdk,targetSdkandminSdkare three different questions: what you build against, what behaviour you opt into, and what devices can install you.- An API diff is generated per level and lists additions, changes and removals. It is the most precise record of the public surface, and it says nothing about behaviour.
- The platform source (
cs.android.com) documents what the framework actually does, including everything the reference docs omit. It is not a contract, but it is the truth. - Deprecation is a warning, not a removal. The API you are told not to use will usually still work for years, which is both a comfort and a trap.
Mental model#
Think of the SDK as a very large public library.
The catalogue is the reference documentation: entries for every book, with a description of what each is for. It is useful for finding things, and it is written by humans, so it lags.
The accessions list is the API diff: what arrived, what changed shelfmark, what was withdrawn this year. It is generated mechanically from the shelves themselves, so it is complete and boring and trustworthy about existence.
The books are the platform source. When the catalogue description disagrees with the book, the book wins.
The librarian's notices are the behaviour-change documents: "from this year, this reading room works differently". They are the only source that tells you what happens when you use an API, as opposed to whether it exists.
Where the analogy breaks#
A library's catalogue and its shelves describe the same objects. The Android SDK has a third thing
that neither describes: the runtime behaviour behind the API, which can change without the
signature changing at all. PackageManager can gain a new restriction on an existing method, and no
API diff will show it. That is why behaviour-change documents exist as a separate genre, and why
"it compiles" is never the same claim as "it works".
The big picture#
Text description of this diagram
The Time Machine at a selected API level. A slider runs from API 1 to API 37. For each level it shows the release card with the version, dessert name where one existed, date and headline features; a developer's-world card listing the UI toolkit, language, IDE, build system, background API and async model in use at that level; and a behaviour card listing the changes introduced at that level, each marked as applying to all apps or only to apps targeting that level. A diff mode compares any two API levels side by side and highlights the rows that differ.
That diagram is the fastest way to build intuition for how much changes per level. Step it from API 21 to 37 and watch the developer's world change: the language, the UI toolkit, the background API. None of those changes appear in a single API diff, because each is spread over several levels.
Deep dive#
The three numbers#
Every Android build declares three API levels, and conflating them is the most common source of confusion about "which Android version does this support".
minSdk is the lowest API level the app will install on. The package manager refuses the
install below it. It is a product decision: raise it and you drop devices; lower it and you must
guard every newer API with a runtime check.
compileSdk is the SDK the app is compiled against. It determines which classes, methods and
constants exist at compile time. It must be greater than or equal to targetSdk, because you
cannot target behaviour you did not compile against.1
targetSdk is the compatibility contract: it tells the platform which behaviour changes the
app has opted into. This is the number that matters most and is understood least. A change marked
"for apps targeting API 36 or higher" does not apply to an app with targetSdk = 35, even on a
device running API 37.
A useful way to hold all three: minSdk is where you run, compileSdk is what you know,
targetSdk is what you promise to handle.
The API diff#
The API diff reports are generated per level and enumerate every class, method and field added, changed or removed between two API levels.3 They are the closest thing to a changelog of the public surface, and they are mechanical: if a method exists in the new stub and not the old one, it appears.
Read an API diff in this order:
- Removals first. These are rare and they are the ones that break builds.
- Signature changes. A parameter type or return type change is a source-incompatible change even when the method name survives.
- Additions by package. This is where you find the new capability, and grouping by package tells you which subsystem is being invested in.
- Deprecations. These are annotations rather than removals, and they signal direction.
What an API diff cannot tell you: whether a method's behaviour changed, whether a new restriction was added to an existing method, or whether a feature is gated behind a flag. Those live in the behaviour-change documents and, ultimately, in the source.
Reading the platform source#
The reference documentation describes intent. The source describes implementation. When they
disagree, source wins, and cs.android.com is the fastest way to get there.4
Three habits make source reading productive:
Read the interface, then the implementation. For a framework API, the public class in
frameworks/base/core/java/ is usually a thin shell over a service in
frameworks/base/services/. A method on PackageManager often does nothing but parcel its
arguments and call a Binder proxy.
Follow the Binder boundary. Every Manager class has a corresponding service. Find the
Stub/Proxy pair generated from AIDL, and the service method is on the other side. That is
usually where the actual policy lives.
Check the annotations. @SystemApi, @hide, @Deprecated and the @RequiresPermission
annotation tell you who the API is for. A @hide method is not part of the contract even though
you can see it in the source, and reflecting into it is exactly what non-SDK restrictions exist to
stop.
Deprecation, removal and the graveyard#
Android almost never removes a public API. Deprecated APIs stay because installed apps depend on
them, and an app that cannot be updated still has to run. AsyncTask was deprecated in Android 11
and is still present. This produces a strange landscape where the documentation tells you not to
use something that works perfectly well and will keep working.
The practical rules:
- Deprecated means "there is a better way", not "this is broken". Migrate when you have a reason; do not panic-migrate.
- Removal happens at the level of behaviour, not signatures. A method can remain and stop doing what it did, especially when a permission or background restriction lands on it.
- Non-SDK restrictions are the real removal mechanism. Since Android 9, the platform restricts
access to non-SDK interfaces, with allowlists, greylists and blocklists. An app that reflected
into
@hideinternals can break without any public API changing.1
Reading release notes without being fooled#
Release notes are marketing documents with a technical appendix. They are useful, and they are structured to make every release look large. A few habits help:
- Separate features from behaviour changes. A new API is optional; a behaviour change is something that happens to you. The behaviour-change page is the one that predicts breakage.
- Check the scope tag. "For apps targeting API N" versus "for all apps" is the single most useful label in modern Android documentation.
- Check the device gate. Some changes only apply on large screens, only on devices with a certain feature, or only when a flag is enabled.
- Check the source when it matters. If a restriction would break your app, verify it against the platform source or a test device rather than trusting a summary.
How it evolved#
The SDK's own shape has evolved alongside the platform. Early releases added whole packages; modern releases add smaller surfaces and spend more words on behaviour.
| API | Version | What changed |
|---|---|---|
| 26 | 8.0 Oreo |
|
| 27 | 8.1 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 31 | 12 |
|
| 32 | 12L |
|
| 33 | 13 Tiramisu |
|
| 34 | 14 Upside Down Cake |
|
| 35 | 15 Vanilla Ice Cream |
|
| 36 | 16 Baklava |
|
| 37 | 17 Cinnamon Bun |
|
The trend across those rows is consistent: since Treble, a growing share of "what changed" is about
targetSdk-gated behaviour rather than new classes. That is not an accident. As the API surface
matures, the leverage moves from adding capability to changing defaults safely.
Under the hood#
- Build.javaVERSION.SDK_INT and the fields the platform reports about itself
- PackageManager.javathe public facade over a very large Binder service
- SystemServiceRegistry.javahow a Context constant becomes a manager object
Curiosity corner#
- The API surface is enormous and grows every year. The API Explorer on this site charts classes added per level from the machine-readable API history; the curve is not linear.
compileSdkcan be higher than the device's API level. You can compile against 37 and run on a device at 30, as long as you guard the calls — which is whatminSdkand runtime checks are for.- Two API levels can share a marketing version. Android 12 is API 31, Android 12L is API 32.
Try it#
adb shell getprop ro.build.version.sdk
adb shell dumpsys package com.android.settings | grep -E "minSdk|targetSdk"
adb shell cmd package resolve-activity --brief android.intent.action.MAINExample output
Illustrative output — package names and numbers differ per device.Build it#
B02 — Intent explorer is a small app that prints the resolved component and the declaring package for an intent, which is the practical version of "read the package manager instead of guessing".
Check yourself#
Check yourself
What does an API level identify?
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docs<uses-sdk> | App architecturedeveloper.android.comaccessed 2026-10-27↩
- docsTarget API level requirements for Google Play appsdeveloper.android.comaccessed 2026-10-27↩
- docsAPI Differences between 35 and 36developer.android.comaccessed 2026-10-27↩
- sourceAndroid Code Search (cs.android.com)cs.android.comaccessed 2026-10-27↩
- docsAndroid releases and versionsdeveloper.android.comaccessed 2026-10-27↩
- sourceAndroid Gerrit code reviewandroid-review.googlesource.comaccessed 2026-10-27↩
- wikiAndroid version historyWikipediaaccessed 2026-10-27↩