Pocket Studio Academy
HomePart 22.3

The AndroidManifest — your app's ID card

Full course9 min read·4 questions

One small XML file is the only part of your app Android reads before running anything. Learn what every line of it declares, why minSdk is no longer in it, and what the manifest merger does with the libraries you use.

The one file Android reads first

Your app is thousands of lines of Kotlin. Android has read none of them.

Before your app runs, and in fact at the moment it is installed, Android reads exactly one file to find out what this app is: AndroidManifest.xml. Everything the system knows about you — your name, your icon, which screen opens first, what you are allowed to ask for — comes from that file and nowhere else.

Get it wrong and your app will not appear on the home screen, or will refuse to start, or will be denied something it needs, and none of those failures will point at your Kotlin. So it is worth nine minutes.

Think of it like this

Think about a shop opening on a high street.

Before a single customer arrives, the owner files a form with the council. Trading name. Which door is the public entrance. What they intend to sell. Whether they need a licence for anything special, like alcohol or late opening.

The council never reads the shop's recipes. It reads the form. If the form says the public entrance is round the back, that is where customers will be sent — regardless of what the shop would prefer.

The is that form, and Android is the council.

The whole file

This is the complete manifest from Dice Duel, the first app you build in Part 5. Not a simplified version — this is the real file, all twenty-six lines.

That is the entire file. There is no hidden extra part — this manifest is enough to describe a complete, installable, publishable Android app.

What is not in the manifest any more

If you look at an Android tutorial written before about 2018, you will see a <uses-sdk> block in the manifest holding minSdkVersion and targetSdkVersion, plus versionCode and versionName up on the <manifest> tag.

Those have all moved into app/build.gradle.kts:

app/build.gradle.ktskts
1defaultConfig {
2  applicationId = "com.nativeworks.diceduel"
3  minSdk = 26
4  targetSdk = 35
5  versionCode = 1
6  versionName = "1.0"
7}

They still end up in the manifest — the injects them during the build — but you write them in one place, in Kotlin, where they can differ between a debug and a release build. Lesson 2.7 is entirely about those SDK numbers, and comes back in Part 6.

Note

This is why copying a manifest snippet off an old forum post so often fails. If you paste a <uses-sdk> block into a modern project, the build tells you to remove it — because Gradle is now the source of truth for that number.

Permissions

Anything genuinely sensitive has to be declared, in public, in this file. If it is not in the manifest, your app simply cannot do it — there is no way to ask at runtime for something you never declared.

Focus Flow, the third app in this course, asks for exactly one:

Focus Flow's manifestxml
1<uses-permission
2  android:name="android.permission.POST_NOTIFICATIONS" />

Declaring a is only half the job. For sensitive ones, you must also ask the user at runtime, and they may say no — your app has to still work when they do. Lesson 5.17 does this properly.

The habit worth forming now: declare the fewest permissions you can. Every one of them is shown to the user before they install, and a list of five for a dice game is a list of five reasons not to bother.

Your manifest is not the manifest that ships

Here is a genuinely surprising fact. The file above is not what ends up inside your .

Every you depend on ships its own tiny manifest. Compose needs a couple of declarations. The lifecycle library installs a startup component. A build step called the combines yours with every one of theirs into a single merged manifest, and that is what gets packaged.

You can read the whole merge decision, in order, in a real file the build writes:

text
app/build/outputs/logs/manifest-merger-debug-report.txt

Open it and you will find lines like this — this is genuine output from building Dice Duel:

text
1MERGED from [androidx.activity:activity-compose:1.9.3]
2MERGED from [androidx.compose.ui:ui-android:1.7.6]
3MERGED from [androidx.lifecycle:lifecycle-process:2.8.7]

Thirty-odd libraries, each contributing a line or two. It is the best possible answer to "where did that thing in my manifest come from?" — and the first place to look when the merger refuses to merge, which is the most common failure in this whole lesson.

Try it in Pocket Studio
  1. Open Pocket Studio, tap Projects, open your Part 0 app.
  2. Tap Editor, open the file tree, and tap app/src/main/AndroidManifest.xml. Read it against the walkthrough above — your file will be almost identical, with a different theme name.
  3. Change the app's visible name. Open app/src/main/res/values/strings.xml and edit the text inside <string name="app_name"> to something new.
  4. Tap Build, press Run, then look at your home screen. The label under the icon has changed — because the manifest pointed at the resource instead of holding the text.
  5. Now break it on purpose. In the manifest, change android:name=".MainActivity" to android:name="MainActivity" — delete the dot.
  6. Press Run and read the failure. Put the dot back.
  7. Finally, open app/build/outputs/logs/manifest-merger-debug-report.txt in the file tree and scroll it. Every MERGED from line is a library adding something to your manifest.
Error Doctor5 common errors
Manifest merger failed : Attribute application@label value=(@string/app_name) from AndroidManifest.xml:8:5-40 is also present at [com.example:widget:1.2.0] AndroidManifest.xml:12:9-41 value=(@string/lib_name). Suggestion: add 'tools:replace="android:label"' to <application> element at AndroidManifest.xml:5:3-25:17 to override.
MeansYou and one of your libraries both set the same attribute to different values, and the merger will not guess which one you meant.
FixDo what the message says. Add xmlns:tools="http://schemas.android.com/tools" to the <manifest> tag, then tools:replace="android:label" on <application>. That declares "mine wins".
android:exported needs to be explicitly specified for element <activity#com.nativeworks.diceduel.MainActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for android:exported when the corresponding component has an intent filter defined.
MeansYour activity has an <intent-filter> but never said whether other apps may start it. Android used to guess; since Android 12 guessing is banned because the guess was a security hole.
FixAdd android:exported="true" to the launcher activity. Any activity that is only started from inside your own app should have android:exported="false".
uses-sdk:minSdkVersion 21 cannot be smaller than version 26 declared in library [androidx.example:example:1.0.0] /…/AndroidManifest.xml as the library might be using APIs not available in 21
MeansA library you added refuses to run on phones as old as yours does. The merger caught the disagreement before the app could crash on somebody's old handset.
FixEither raise minSdk in app/build.gradle.kts to at least the number the library demands, or find an older version of that library. This course uses minSdk = 26, which most modern libraries accept.
AAPT: error: resource string/app_name (aka com.nativeworks.diceduel:string/app_name) not found.
MeansThe manifest asked for a resource by name and nothing with that name exists. Usually a typo in the manifest, or strings.xml was renamed or emptied.
FixOpen app/src/main/res/values/strings.xml and confirm there is a line <string name="app_name">…</string>. The name in the manifest must match it exactly, capitals included.
Manifest merger failed with multiple errors, see logs
MeansMore than one thing went wrong, so the summary line tells you nothing on its own. The details are in the full build output, above this line.
FixScroll up in the build output to the first line beginning Error: — that is the real problem. Fixing it usually clears the rest.
Recap
  • AndroidManifest.xml is the only part of your app Android reads before running it. It declares your name, icon, theme, screens and .
  • @string/… and @mipmap/… point at instead of holding values, which is what makes renaming and translating possible.
  • An is invisible to Android unless it is declared here, and needs android:exported="true" plus a MAIN/LAUNCHER to get an icon.
  • minSdk, targetSdk and versionCode moved out of this file and into app/build.gradle.kts.
  • The combines your file with every library's, and writes a full report you can read.
  • Next: the res folder — how strings, colours and pictures are stored so Android can swap them for dark mode, for another language, or for a bigger screen.