xamarin-android-migration
Xamarin.Android → .NET for Android Migration
Use this skill when migrating a Xamarin.Android native app (not Xamarin.Forms) to .NET for Android.
Field-tested advice: Android migration is significantly harder than iOS. Expect more UI bugs, OEM-specific rendering differences, and issues not reproducible on emulators. Test on physical devices.
Migration Workflow Overview
- Create a new .NET for Android project
- Convert project file to SDK-style format
- Update MSBuild properties (ABIs, AOT, etc.)
- Update AndroidManifest.xml
- Copy code and resources
- Update NuGet dependencies
- Migrate binding libraries (if applicable)
- Set up Xamarin.Essentials replacement (if applicable)
- Handle encoding changes
- Build, test on physical devices
Migration Strategy
Create a new .NET for Android project with the same name, then copy code into it. This is simpler than editing the existing project file.
dotnet new android --output MyAndroidApp --packageName com.mycompany.myandroidapp
Step 1 — SDK-Style Project File
A .NET for Android project uses the SDK-style format:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationId>com.companyname.myapp</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
</PropertyGroup>
</Project>
For library projects, omit <OutputType> or set it to Library.
Replace
net8.0-androidwithnet9.0-androidornet10.0-androidas needed. The TFM denotes the project as .NET (e.g.,net8.0-androidmaps to Android API 34).
Step 2 — MSBuild Property Changes
| Xamarin.Android Property | .NET for Android Equivalent | Notes |
|---|---|---|
AndroidSupportedAbis |
RuntimeIdentifiers |
See conversion table below |
AotAssemblies |
RunAOTCompilation |
Deprecated in .NET 7 |
AndroidClassParser |
(default: class-parse) |
jar2xml not supported |
AndroidDexTool |
(default: d8) |
dx not supported |
AndroidCodegenTarget |
(default: XAJavaInterop1) |
XamarinAndroid not supported |
AndroidManifest |
(default: AndroidManifest.xml in root) |
No longer in Properties/ |
DebugType |
(default: portable) |
full and pdbonly not supported |
MonoSymbolArchive |
(removed) | mono-symbolicate not supported |
MAndroidI18n |
System.Text.Encoding.CodePages NuGet |
See encoding section |
AndroidUseIntermediateDesignerFile |
(default: True) |
|
AndroidBoundExceptionType |
(default: System) |
Aligns with .NET semantics |
ABI → RuntimeIdentifier Conversion
AndroidSupportedAbis |
RuntimeIdentifiers |
|---|---|
armeabi-v7a |
android-arm |
arm64-v8a |
android-arm64 |
x86 |
android-x86 |
x86_64 |
android-x64 |
<!-- Xamarin.Android -->
<AndroidSupportedAbis>armeabi-v7a;arm64-v8a;x86;x86_64</AndroidSupportedAbis>
<!-- .NET for Android -->
<RuntimeIdentifiers>android-arm;android-arm64;android-x86;android-x64</RuntimeIdentifiers>
Step 3 — AndroidManifest.xml Changes
Remove <uses-sdk> from AndroidManifest.xml. Use MSBuild properties instead:
<!-- BEFORE (Xamarin.Android AndroidManifest.xml) -->
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<!-- AFTER (.NET for Android csproj) -->
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework> <!-- targetSdkVersion -->
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> <!-- minSdkVersion -->
</PropertyGroup>
TargetFramework maps to android:targetSdkVersion — it's set automatically
at build time from the TFM (e.g., net8.0-android34.0 → API 34).
Step 4 — Copy Code and Resources
- Copy source files, layouts, drawables, and other resources from the Xamarin project to the new project (same folder structure).
- Copy project properties (conditional compilation, code signing) by comparing project files side-by-side.
- Delete
Resource.designer.cs— it will be regenerated. - Delete all
bin/andobj/folders.
Step 5 — Update NuGet Dependencies
| Compatible Frameworks | Incompatible |
|---|---|
net8.0-android |
|
monoandroid |
|
monoandroidXX.X |
Android is unique: NuGet packages targeting
monoandroidstill work on .NET for Android. .NET Standard libraries without incompatible dependencies are also compatible.
If no compatible version exists:
- Recompile with .NET TFMs (if you own it)
- Look for a preview .NET version
- Replace with a .NET-compatible alternative
Step 6 — Android Binding Library Migration
For binding libraries, create a new project and copy bindings:
dotnet new android-bindinglib --output MyJavaBinding
Key changes:
- Use SDK-style project format
@(InputJar),@(EmbeddedJar), or@(LibraryProjectZip)auto-enable$(AllowUnsafeBlocks)AndroidClassParserdefaults toclass-parse(nojar2xml)
Step 7 — Xamarin.Essentials in Native Apps
If your Xamarin.Android app used Xamarin.Essentials, the functionality is now part of .NET MAUI's platform integration layer:
- Remove the
Xamarin.EssentialsNuGet package - Add
<UseMauiEssentials>true</UseMauiEssentials>to your project file - Initialize in your Activity:
using Microsoft.Maui.ApplicationModel;
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
Platform.Init(this, savedInstanceState);
}
}
- Update
usingdirectives (see namespace table):
| Xamarin.Essentials | .NET MAUI Namespace |
|---|---|
| App actions, permissions, version tracking | Microsoft.Maui.ApplicationModel |
| Contacts, email, networking | Microsoft.Maui.ApplicationModel.Communication |
| Battery, sensors, flashlight, haptics | Microsoft.Maui.Devices |
| Media picking, text-to-speech | Microsoft.Maui.Media |
| Clipboard, file sharing | Microsoft.Maui.ApplicationModel.DataTransfer |
| File picking, secure storage, preferences | Microsoft.Maui.Storage |
- Override
OnRequestPermissionsResultin every Activity:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
Step 8 — Encoding Changes
Replace MAndroidI18n with the System.Text.Encoding.CodePages NuGet package:
// At app startup
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Step 9 — AOT Compilation
Release builds default to profiled AOT:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<RunAOTCompilation>true</RunAOTCompilation>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
To disable AOT, explicitly set both to false.
Step 10 — Configuration Files
There is no support for .dll.config or .exe.config files in .NET for Android.
<dllmap> configuration elements are not supported in .NET Core.
If your app uses System.Configuration.ConfigurationManager, note it has never been
supported on Android. Migrate to appsettings.json or platform preferences.
.NET CLI Support
| Command | Description |
|---|---|
dotnet new android |
Create new app |
dotnet new androidlib |
Create class library |
dotnet new android-bindinglib |
Create binding library |
dotnet new android-activity --name LoginActivity |
Add activity |
dotnet new android-layout --name MyLayout --output Resources/layout |
Add layout |
dotnet build |
Build (produces .apk/.aab) |
dotnet run --project MyApp.csproj |
Deploy and run on device/emulator |
dotnet publish |
Publish for distribution |
Note:
dotnet buildproduces a runnable.apk/.aabdirectly (unlike desktop .NET wherepublishis typically needed). Inside IDEs, theInstalltarget handles deployment instead.
Build and Troubleshoot
- Delete all
bin/andobj/folders - Delete
Resource.designer.cs - Build and fix compiler errors iteratively
- Check
android:versionCodeandandroid:versionName— these can now live in the csproj asApplicationVersionandApplicationDisplayVersion
Platform-Specific Gotchas
- OEM rendering differences: Android OEMs customize rendering in ways not reproducible on emulators. Always test on physical devices from multiple vendors.
- Shadow rendering: Varies across OEMs and API levels. Implement shadows in
platform-specific handler code rather than relying on the cross-platform
Shadowproperty alone. - Android Wear: Referencing an Android Wear project from an Android app is not supported in .NET for Android.
API Currency Note
If your migrated app will also adopt .NET MAUI controls (e.g., via UseMaui),
check the maui-current-apis skill for deprecated MAUI APIs to avoid
(ListView, Frame, Device.*, etc.).
Quick Checklist
- ☐ Created new .NET for Android project (
dotnet new android) - ☐ Set
TargetFrameworktonet8.0-android(or later) - ☐ Set
SupportedOSPlatformVersionfor minimum SDK - ☐ Converted
AndroidSupportedAbis→RuntimeIdentifiers - ☐ Removed
<uses-sdk>from AndroidManifest.xml - ☐ Copied source, resources, and project properties
- ☐ Deleted
Resource.designer.cs,bin/,obj/ - ☐ Updated NuGet dependencies
- ☐ Added
UseMauiEssentialsif using Essentials - ☐ Replaced
MAndroidI18nwithSystem.Text.Encoding.CodePages - ☐ Verified AOT settings for Release builds
- ☐ Tested on physical Android device(s)