Start Debugging

Eine .NET MAUI Android-App auf API-Level 36 migrieren

Google Play verlangt seit 2026-08-31 das Ziel-API-Level 36, Verlängerungen laufen bis 2026-11-01. Hier ist der vollständige .NET MAUI Weg von net9.0-android zu API 36: der Wechsel des Target Frameworks, das fest eingetragene uses-sdk, das Sie unbemerkt auf dem alten Level hält, Edge-to-Edge ohne Abwahlmöglichkeit, Predictive Back und die Regeln für große Bildschirme.

Die Build-Änderung ist eine Zeile. Die Verhaltensänderungen sind die Migration. Google Play verlangt seit 2026-08-31 das Ziel-API-Level 36 für neue Apps und App-Updates, mit einer app-bezogenen Verlängerung über die Play Console bis 2026-11-01. Wenn Ihr Update diese Woche abgelehnt wurde, liegt es daran. In einer .NET MAUI App ist das Ziel-API-Level keine Manifest-Einstellung, die Sie bearbeiten: Es leitet sich aus der Android-Plattformversion in Ihrem TargetFramework ab, und .NET 9 endet bei API 35. Das heißt, dies ist ein Upgrade des .NET SDK auf .NET 10 (oder .NET 11), keine Manifest-Korrektur. Planen Sie einen Tag für eine kleine App und einen Sprint für alles mit fixierter Ausrichtung, eigenem Zurück-Button oder handoptimierten Insets. Diese Anleitung zielt auf .NET 10 mit .NET MAUI 10.0.100 (veröffentlicht am 2026-08-20) und benennt die Stellen, an denen .NET 11 abweicht.

Warum genau das Ziel-Level geprüft wird

Was bricht

BereichÄnderung bei Ziel-API 36Schweregrad
Edge-to-EdgewindowOptOutEdgeToEdgeEnforcement ist veraltet und wird auf Android-16-Geräten ignorierthoch
.NET MAUI Safe AreasContentPage.SafeAreaEdges steht ab .NET 10 standardmäßig auf None, Seiten laufen also randloshoch
Predictive BackAnimationen für Zurück-zum-Homescreen und zwischen Activities sind standardmäßig aktiv; OnBackPressed wird nicht aufgerufenhoch
Große Bildschirmeandroid:screenOrientation, resizableActivity, minAspectRatio und maxAspectRatio werden ab sw600dp ignorierthoch (Tablets, Foldables)
.NET SDKAPI 36 benötigt net10.0-android oder neuer; die .NET 9 Workload endet bei API 35hoch
Minimale API.NET 11 hebt die Untergrenze von API 21 auf API 24mittel (nur .NET 11)
Textdarstellungandroid:elegantTextHeight ist veraltet und wird ignoriertniedrig
ZeitplanungScheduledExecutorService.scheduleAtFixedRate holt höchstens eine verpasste Ausführung nachniedrig
GesundheitssensorenBODY_SENSORS wird durch granulare android.permissions.health Berechtigungen ersetztniedrig (außer Sie lesen die Herzfrequenz)

Die ersten beiden Zeilen verstärken sich gegenseitig. Das Upgrade auf .NET 10, um API 36 zu erreichen, ändert im selben Commit auch den Safe-Area-Standard von .NET MAUI selbst. Eine App, die unter .NET 9 mit Ziel 35 gut aussah, kann also aus zwei unabhängigen Gründen mit der Titelleiste unter der Statusleiste herauskommen.

Checkliste vor dem Start

Migrationsschritte

  1. Stellen Sie fest, worauf Sie heute tatsächlich zielen. Lesen Sie nicht die csproj, sondern das zusammengeführte Manifest, das der Build erzeugt:

    dotnet build -f net9.0-android -c Release
    grep -o 'targetSdkVersion="[0-9.]*"' obj/Release/net9.0-android/AndroidManifest.xml

    Prüfung: Sie erhalten genau eine Zahl. Ist sie kleiner als die Android-Plattformversion in Ihrem TargetFramework, fixiert etwas den Wert, und Schritt 3 ist für Sie der wichtigste.

  2. Setzen Sie das Target Framework auf .NET 10. Die Android-Plattformversion im TFM wird zur targetSdkVersion, diese eine Änderung ist also die eigentliche Migration:

    <!-- .csproj, .NET 10, .NET MAUI 10.0.100 -->
    <PropertyGroup>
      <TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
      <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
    </PropertyGroup>

    Ein blankes net10.0-android löst zu API 36 auf, dem dokumentierten Standard von .NET 10. Pinnen Sie es explizit als net10.0-android36.0, wenn der Build beim späteren Wechsel auf .NET 11 lieber fehlschlagen als abdriften soll, denn .NET for Android hat API 37 in .NET 11 Preview 5 als stabil eingestuft und lässt .NET 11 Projekte nun standardmäßig auf net11.0-android37 zielen. $(SupportedOSPlatformVersion) ist eine andere Achse: Daraus wird minSdkVersion, und mit der Play-Anforderung hat das nichts zu tun.

    Prüfung: Neu bauen und den grep aus Schritt 1 gegen obj/Release/net10.0-android/AndroidManifest.xml wiederholen. Es muss targetSdkVersion="36" ausgeben.

  3. Löschen Sie jedes fest eingetragene uses-sdk aus Ihrem Manifest. Das ist der häufigste Grund, warum Schritt 2 wirkungslos erscheint. .NET for Android schreibt targetSdkVersion nur, wenn das Vorlagen-Manifest noch keine hat, und ein expliziter Wert gewinnt uneingeschränkt (ManifestDocument.cs):

    <!-- Platforms/Android/AndroidManifest.xml: delete the uses-sdk line entirely -->
    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
      <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
      <application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true" />
    </manifest>

    Microsofts eigene Anleitung zu XA5207 empfahl genau dieses Element, um ein Ziel-Level über ein SDK-Upgrade hinweg zu halten. Entsprechend viele Projekte aus der Xamarin.Forms Zeit tragen es noch mit sich. Die aktuelle .NET MAUI Vorlage enthält gar kein uses-sdk Element, und genau dieser Zustand ist gewünscht.

    Prüfung: grep -c uses-sdk Platforms/Android/AndroidManifest.xml liefert 0, und das zusammengeführte Manifest zeigt weiterhin targetSdkVersion="36".

  4. Entscheiden Sie sich für eine Edge-to-Edge Strategie, denn Sie haben kein Stimmrecht mehr. Bei Ziel 36 ist das Attribut windowOptOutEdgeToEdgeEnforcement auf Android-16-Geräten veraltet und deaktiviert. Wenn es in Platforms/Android/Resources/values/styles.xml steht, löschen Sie es. Wählen Sie danach pro Seite einen SafeAreaEdges Wert, statt den .NET 10 Standard None hinzunehmen:

    <!-- .NET MAUI 10.0.100: ContentPage defaults to SafeAreaEdges="None" -->
    <ContentPage SafeAreaEdges="Container">
        <Grid SafeAreaEdges="Container" RowDefinitions="Auto,*">
            <Label Text="Not under the status bar" />
        </Grid>
    </ContentPage>

    Container reproduziert das .NET 9 Verhalten, sich von Systemleisten und Display-Aussparungen fernzuhalten. All weicht zusätzlich der Tastatur aus, was Sie brauchen, wenn Sie sich auf das Android-Platform-Specific WindowSoftInputModeAdjust.Resize verlassen haben. None ist die immersive Variante und eine bewusste Entscheidung, kein Standard, den Sie versehentlich erben sollten.

    Prüfung: Auf einem Android-16-Gerät überlappen Statusleiste und Gestennavigationsleiste auf Ihren drei wichtigsten Bildschirmen kein antippbares Steuerelement, im hellen wie im dunklen Design.

  5. Reparieren Sie eigene Zurück-Logik, bevor Predictive Back sie schluckt. Bei Ziel 36 sind die Predictive-Back-Animationen standardmäßig aktiv, onBackPressed() wird nicht aufgerufen und KeyEvent.KEYCODE_BACK nicht ausgeliefert. Eine Activity-Überschreibung wie diese läuft nicht mehr:

    // Broken at targetSdkVersion 36 on Android 16
    public override void OnBackPressed()
    {
        if (_hasUnsavedChanges) { ShowConfirmDialog(); return; }
        base.OnBackPressed();
    }

    Behandeln Sie das stattdessen in der Navigationsschicht von .NET MAUI, die plattformübergreifend weiter funktioniert:

    // .NET MAUI 10.0.100, cross-platform
    protected override bool OnBackButtonPressed()
    {
        if (!_hasUnsavedChanges)
            return base.OnBackButtonPressed();
    
        Dispatcher.Dispatch(async () => await DisplayAlertAsync("Discard changes?", "...", "OK"));
        return true; // handled
    }

    Die Android-Notlösung ist android:enableOnBackInvokedCallback="false" an <application> oder an einer einzelnen <activity>, und sie ist ein Notbehelf, keine Lösung.

    Prüfung: Vom Bildschirmrand wischen und halten. Sie sollten die Vorschau-Animation sehen, und beim Loslassen sollte passieren, was Ihr Handler vorsieht.

  6. Prüfen Sie fixierte Ausrichtung und feste Seitenverhältnisse. Auf Displays ab sw600dp ignoriert Android bei Ziel 36 android:screenOrientation, android:resizableActivity, android:minAspectRatio und android:maxAspectRatio sowie SetRequestedOrientation zur Laufzeit. In .NET MAUI bedeutet das meist ein Attribut an MainActivity:

    // Ignored on sw600dp+ displays at targetSdkVersion 36
    [Activity(ScreenOrientation = ScreenOrientation.Portrait, /* ... */)]
    public class MainActivity : MauiAppCompatActivity { }

    Die vorübergehende Abwahl ist eine Manifest-Eigenschaft, und Google hat erklärt, dass sie ab API-Level 37 nicht mehr greift:

    <application>
      <property android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
                android:value="true" />
    </application>

    Prüfung: Auf einem Tablet- oder Foldable-Emulator ausführen und drehen. Ist das Layout im Querformat unbrauchbar, reparieren Sie das Layout, denn die Abwahl kauft Ihnen ein Jahr.

  7. Aktualisieren Sie CI, damit dort nicht gegen eine fehlende Plattform gebaut wird. Fehlt API 36 auf einem Agent, erscheint XA5207, und die Lösung ist ein Target, kein Portal-Download:

    dotnet build -t:InstallAndroidDependencies -f net10.0-android \
      -p:AndroidSdkDirectory="$ANDROID_HOME" \
      -p:AcceptAndroidSDKLicenses=true

    Das Argument -f ist Pflicht, sonst meldet MSBuild MSB4057: The target "InstallAndroidDependencies" does not exist in the project.

    Prüfung: Ein sauberer CI-Lauf aus einem leeren SDK-Cache erzeugt ein signiertes AAB ohne XA5207.

Prüfliste

Rollback-Plan

Das Zurücksetzen des TargetFramework auf net9.0-android stellt das alte Ziel-Level und das alte Safe-Area-Verhalten von .NET MAUI wieder her, und es ist ein sauberer Revert, solange Sie nicht zusätzlich .NET 10 APIs übernommen haben. Nicht zurückrollen lässt sich die Play-Seite: Sobald Sie ein AAB mit Ziel 36 ausgeliefert haben, können Sie danach kein niedrigeres Ziel-Level mehr auf denselben Track veröffentlichen, weil Play die Untergrenze bei jedem Upload durchsetzt. Behandeln Sie den internen Track als Ihr Rollback-Fenster und die Freigabe in Produktion als Einbahnstraße.

Fallstricke, die echte Zeit kosten

Verwandt

Quellen

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Zurück