Publishing Android Apps
This guide covers publishing Miko apps for Android using the Miko.Android host. The output is an APK (for direct installation) or AAB (Android App Bundle, for Google Play).
Requirements
- .NET 10 SDK with the
androidworkload installed (dotnet workload install android) - Android SDK (installed automatically by the workload)
- A signing keystore (required for Release builds and Google Play)
Build a Release AAB
Google Play requires apps to be uploaded as an Android App Bundle (.aab file):
dotnet publish MyApp.Android/MyApp.Android.csproj \
-c Release \
-f net10.0-android \
-p:AndroidPackageFormat=aabThe output is bin/Release/net10.0-android/publish/com.example.myapp-Signed.aab.
Build a Release APK
To build an APK for direct installation or testing:
dotnet publish MyApp.Android/MyApp.Android.csproj \
-c Release \
-f net10.0-android \
-p:AndroidPackageFormat=apkThe output is bin/Release/net10.0-android/publish/com.example.myapp-Signed.apk.
Signing
Android requires all Release builds to be signed. The templates do not include a keystore by default. You must create one and configure signing properties.
Create a keystore
keytool -genkey -v \
-keystore myapp.keystore \
-alias myapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000Keep the keystore file and password safe. If you lose them, you cannot update your app on Google Play.
Configure signing in the csproj
Add the following properties to MyApp.Android.csproj:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<AndroidKeyStore>true</AndroidKeyStore>
<AndroidSigningKeyStore>$(MSBuildProjectDirectory)/myapp.keystore</AndroidSigningKeyStore>
<AndroidSigningKeyAlias>myapp</AndroidSigningKeyAlias>
<AndroidSigningKeyPass>$(KEYSTORE_PASSWORD)</AndroidSigningKeyPass>
<AndroidSigningStorePass>$(KEYSTORE_PASSWORD)</AndroidSigningStorePass>
</PropertyGroup>Important: Never commit passwords to source control. Pass them via environment variables:
export KEYSTORE_PASSWORD="your_password_here"
dotnet publish MyApp.Android/MyApp.Android.csproj -c Release -f net10.0-android -p:AndroidPackageFormat=aabApp metadata
The following properties in MyApp.Android.csproj control the app's identity and versioning:
<PropertyGroup>
<ApplicationId>com.example.myapp</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0.0</ApplicationDisplayVersion>
</PropertyGroup>ApplicationId— the package name (must matchCFBundleIdentifierin your iOS project if you have one). Use--appIdwhen scaffolding to set both:dotnet new miko-multiplatform --appId com.mycompany.myappApplicationVersion— integer version code, incremented for each Play Store release (users never see this)ApplicationDisplayVersion— the public version string (e.g.1.0.0,1.2.3-beta)
AndroidManifest.xml
The template's AndroidManifest.xml declares:
targetSdkVersion="36"(Android 16) — Google Play requires new apps and updates to target a recent API level; check the Play Console for the current requirementminSdkVersion="26"(Android 8.0) — Miko's minimum supported versionINTERNETpermission — required for network access in Release builds (Debug builds add it implicitly)
<uses-sdk android:minSdkVersion="26" android:targetSdkVersion="36" />
<uses-permission android:name="android.permission.INTERNET" />MainActivity declares ConfigurationChanges so that rotation, dark mode, font scale and display-size changes do not recreate the Activity (which would rebuild the app and lose the navigation stack). Keep it if you edit the attribute:
[Activity(
...,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.ScreenLayout
| ConfigChanges.SmallestScreenSize | ConfigChanges.Density | ConfigChanges.UiMode
| ConfigChanges.FontScale | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]App icon and launch screen
App icon
The template includes a default adaptive icon in Resources/mipmap-*/:
appicon_foreground.png— foreground layer (with transparency)appicon_background.png— background layer (solid color or image)appicon.png— fallback for launchers that do not support adaptive icons
Replace these files with your own artwork, keeping the file names and pixel sizes. The default icon is the .NET bot from the .NET MAUI template and is only a placeholder.
MainActivity already references them:
[Activity(
Icon = "@mipmap/appicon",
RoundIcon = "@mipmap/appicon_round",
...)]Launch screen
Android 12 and later generate the launch screen automatically from the app icon. The template does not include a custom SplashScreen theme. If you need a custom splash screen, add a theme and reference it in the Activity attribute.
Permissions and features
If your app uses Miko.Native capabilities (camera, location, notifications, etc.), you must declare the corresponding permissions in AndroidManifest.xml. For example:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />See the Android permissions guide for the full list.
16 KB page alignment
Google Play requires native libraries in apps targeting API 35+ to be aligned to 16 KB page boundaries. The native libraries Miko ships (SkiaSharp, HarfBuzzSharp) are 16 KB aligned. If a build reports XA0141, another dependency brought in a native library that is not; check which .so the warning names.
Testing
Test the signed AAB or APK on a real device or emulator:
# Install and run (device or emulator must be connected via adb)
adb install bin/Release/net10.0-android/publish/com.example.myapp-Signed.apk
# The Activity class name is generated; ask the package manager for it
adb shell am start -n "$(adb shell cmd package resolve-activity --brief com.example.myapp | tail -1)"Or open the project in Visual Studio or Rider and deploy to a connected device.
Upload to Google Play
- Sign in to the Google Play Console
- Create a new app or select an existing one
- Under Release > Production (or Internal testing / Closed testing / Open testing), create a new release
- Upload the signed AAB file
- Fill in the release notes and roll out
Google Play will generate APKs optimized for each device configuration from your AAB.