¿Abriste Play Console estas semanas y te encontraste con un aviso rojo de "Target API level" sobre tu app .NET MAUI? Si es así, no estás solo: el 31 de agosto de 2026 Google Play empezó a exigir Android 16 (API 36) para apps nuevas y actualizaciones, y la prórroga que se podía pedir terminó el 1 de noviembre. En otras palabras: si todavía no actualizaste, hoy no puedes subir una nueva versión.
Esto no es nuevo. Desde que empecé con Xamarin en 2017, cada año ha tocado subir el targetSdkVersion. Lo que sí cambió es cómo se hace en .NET: ya no es un desplegable en las propiedades del proyecto, sino que depende de tu TargetFramework. Y hay una trampa en el manifiesto que vale la pena conocer.
Hoy vamos a ver qué exige Google Play, qué versión de .NET necesitas, qué cambiar en el .csproj y qué comportamientos y permisos de Android 16 tienes que revisar antes de subir tu AAB.
¿Qué exige Google Play exactamente?

Google tiene dos reglas distintas y es fácil confundirlas:
| Regla | Qué exige | Qué pasa si no cumples |
|---|---|---|
| Apps nuevas y actualizaciones | Apuntar a API 36 (Android 16) desde el 31/08/2026 | No puedes subir la nueva versión |
| Apps existentes | Apuntar al menos a API 35 (Android 15) | Tu app deja de aparecer para usuarios nuevos con Android más reciente que tu target |
Hay excepciones por tipo de dispositivo: Wear OS y Android Automotive OS piden API 35 para actualizaciones, y Android TV y Android XR piden API 34. Si tu app es solo para teléfonos y tablets, la regla que te aplica es la de API 36.
Ojo: los usuarios que ya tienen tu app instalada no la pierden. El problema es que no puedes publicar correcciones ni funciones nuevas, y eso, en la práctica, es como tener la app congelada.
Google sube este requisito cada año, más o menos un año después de que sale cada versión de Android. A mi parecer, lo más sano es asumir que en 2027 va a tocar API 37, aunque al escribir esto Google todavía no lo ha anunciado.
La clave en .NET: el target API viene de tu TargetFramework
En Xamarin.Android elegías "Compile using Android version" y "Target Android version" por separado. En .NET for Android, el nivel de API sale del TFM (net10.0-android, net11.0-android, etc.). Si no pones número, el SDK usa el último nivel estable que soporta esa versión de .NET:

| Versión de .NET | API por defecto con netX.0-android |
¿Cumple Google Play hoy? | Soporte de .NET MAUI |
|---|---|---|---|
| .NET 8 | 34 (Android 14) | ❌ | Terminó el 14/05/2025 |
| .NET 9 | 35 (Android 15) | ❌ (solo sirve para seguir visible) | Terminó el 12/05/2026 |
| .NET 10 | 36 (Android 16) | ✅ | Hasta el 11/05/2027 |
| .NET 11 | 37 (Android 17) | ✅ | Se lanza en .NET Conf (10–13 de noviembre de 2026) |
La conclusión es directa: si tu app sigue en .NET 8 o .NET 9, tienes que pasar al menos a .NET 10. No hay un "parche" para que .NET 9 apunte a API 36: la versión de .NET define qué bindings de Android tienes disponibles.
Importante: .NET MAUI 9 ya no tiene soporte desde mayo de 2026. Aunque el requisito de Google no existiera, quedarte ahí ya era mala idea.
Paso a paso: actualizar el .csproj
1. Cambia el TargetFramework
Este es el cambio principal. En un proyecto .NET MAUI típico:
<PropertyGroup>
<!-- Antes: net9.0-android → API 35 -->
<!-- Ahora: net10.0-android → API 36 -->
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
</PropertyGroup>
Si prefieres que el nivel de API quede explícito y no cambie solo cuando actualices el SDK, puedes ponerlo en el TFM:
<!-- Fija API 36 aunque el SDK traiga una versión más nueva -->
<TargetFrameworks>net10.0-android36.0;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
Lo que más me gusta de dejarlo sin número es que, al pasar a .NET 11, subes a API 37 sin tocar nada más. Lo que no me gusta es justo eso: que el cambio de comportamiento te llegue "de regalo" sin darte cuenta. Para apps en producción, yo lo dejaría explícito.
2. Actualiza los paquetes NuGet
Con el cambio de TFM, actualiza también Microsoft.Maui.Controls y el resto de paquetes a versiones compatibles con .NET 10 (CommunityToolkit.Maui, SkiaSharp, Syncfusion, etc.). La mayoría de los errores de compilación después del salto vienen de aquí, no del API de Android.
3. Revisa SupportedOSPlatformVersion (es otra cosa)
Mucha gente confunde estas dos propiedades:
| Propiedad | Qué controla | En el manifiesto |
|---|---|---|
TargetFramework (net10.0-android) |
Contra qué API compilas y a cuál apuntas | targetSdkVersion |
SupportedOSPlatformVersion |
Versión mínima de Android donde se instala tu app | minSdkVersion |
La documentación de .NET for Android lo dice claro: SupportedOSPlatformVersion fija el mínimo y "no representa la política de Google Play". O sea, no tienes que subir el mínimo para cumplir con Play.
Eso sí, si piensas pasar a .NET 11, ten en cuenta que el mínimo sube a API 24 (Android 7.0), porque CoreCLR lo requiere. Es un cambio importante documentado oficialmente:
<!-- Necesario en .NET 11: el mínimo pasó de 21 a 24 -->
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
Si quieres más contexto sobre ese cambio de runtime, te lo conté en CoreCLR en .NET 11: adiós Mono.
4. La trampa: el targetSdkVersion del AndroidManifest.xml
Aquí está el detalle que más dolores de cabeza da en proyectos migrados desde Xamarin. Abre Platforms/Android/AndroidManifest.xml y busca esto:
<!-- ❌ Si tienes esto, el TFM no sirve de nada -->
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
Revisando el código de .NET for Android (ManifestDocument.cs), el comportamiento es este: si el manifiesto no trae targetSdkVersion, el build lo agrega con el nivel de tu TFM; pero si ya lo trae, se respeta el valor que tú pusiste. Es decir, puedes compilar con net10.0-android y aun así subir un AAB que apunta a API 33.
La señal de alerta es la advertencia XA1006: "The TargetFrameworkVersion (Android API level 36) is higher than the targetSdkVersion (33)". Si la ves en tu build, no la ignores.
La solución más simple es borrar los atributos y dejar que el TFM mande:
<!-- ✅ Sin uses-sdk: minSdkVersion sale de SupportedOSPlatformVersion
y targetSdkVersion sale del TargetFramework -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon"
android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

5. Verifica el resultado en el paquete final
No te quedes con lo que dice el .csproj: revisa lo que realmente va a recibir Google. Con las herramientas del SDK de Android:
apkanalyzer manifest target-sdk bin/Release/net10.0-android/com.empresa.miapp-Signed.apk
bundletool dump manifest --bundle bin/Release/net10.0-android/com.empresa.miapp-Signed.aab | findstr targetSdkVersion
También puedes preguntarle a MSBuild qué nivel está usando tu proyecto:
dotnet build -f net10.0-android -getProperty:TargetPlatformVersion
Y, por supuesto, Play Console te dice el target API de cada versión en el explorador de app bundles antes de enviarla a revisión.
Qué cambia en tu app al apuntar a Android 16
Subir el número es la parte fácil. Lo importante es que, al apuntar a API 36, tu app empieza a recibir los cambios de comportamiento de Android 16 en dispositivos con Android 16. Estos son los que, a mi parecer, más afectan a una app .NET MAUI:
| Cambio en Android 16 | ¿Te afecta en MAUI? | Qué hacer |
|---|---|---|
| Se desactiva la exclusión de edge-to-edge | Sí, si usabas windowOptOutEdgeToEdgeEnforcement |
Quitarla y manejar el área segura |
| Predictive back activado por defecto | Sí, si sobrescribes OnBackPressed |
Usar los eventos de ciclo de vida de MAUI |
| Se ignoran orientación y redimensionado en pantallas grandes | Sí, si fijas ScreenOrientation |
Probar en tablets y plegables |
BODY_SENSORS → permisos de salud granulares |
Solo si lees sensores de salud | Cambiar el permiso |
elegantTextHeight se ignora |
Rara vez | Revisar textos en árabe, tailandés, etc. |
Edge-to-edge sin escapatoria
En Android 15 todavía podías poner windowOptOutEdgeToEdgeEnforcement en tu tema para que el contenido no se dibujara detrás de las barras del sistema. En Android 16, si apuntas a API 36, ese atributo está deprecado y desactivado. Quítalo.
La buena noticia es que .NET MAUI 10 ya trabaja en modo edge-to-edge (MauiAppCompatActivity llama a WindowCompat.SetDecorFitsSystemWindows(Window, false)) y trae la propiedad SafeAreaEdges para controlarlo. Ojo con este cambio importante: en .NET 9 un ContentPage en Android evitaba las barras del sistema por defecto, y en .NET 10 el valor por defecto es None. Si quieres el comportamiento anterior:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MiApp.Views.InicioPage"
SafeAreaEdges="Container">
<!-- El contenido ya no queda detrás de la barra de estado ni de navegación -->
</ContentPage>
Ojo: si usabasWindowSoftInputModeAdjust.Resizepara que el teclado empujara el contenido, la documentación recomiendaSafeAreaEdges="All"en esa página.
Predictive back: deja de sobrescribir OnBackPressed
Con API 36, Android activa por defecto las animaciones de "predictive back", deja de llamar a onBackPressed y ya no envía KEYCODE_BACK. Si en tu MainActivity tienes algo como esto, en Android 16 simplemente no se va a ejecutar:
// ❌ En Android 16 con target API 36, esto ya no se llama
public override void OnBackPressed()
{
// lógica personalizada...
base.OnBackPressed();
}
En .NET MAUI 10, MauiAppCompatActivity registra un callback de predictive back (desde Android 13) que dispara los mismos eventos de ciclo de vida que antes. Así que el camino correcto es usar esos eventos en MauiProgram.cs:
using Microsoft.Maui.LifecycleEvents;
builder.ConfigureLifecycleEvents(events =>
{
#if ANDROID
events.AddAndroid(android => android.OnBackPressed(activity =>
{
// Devuelve true si ya manejaste el "atrás" y no quieres que se propague
if (MiServicioDeNavegacion.HayModalAbierto)
{
MiServicioDeNavegacion.CerrarModal();
return true;
}
return false; // Deja que MAUI haga la navegación normal
}));
#endif
});
Y si solo necesitas interceptar el botón en una página, Page.OnBackButtonPressed() sigue siendo la opción más sencilla. Si quieres más detalles sobre cómo poner código específico de Android sin ensuciar todo el proyecto, tengo un post sobre código específico de cada plataforma en .NET MAUI.
Importante: existe una exclusión temporal conandroid:enableOnBackInvokedCallback="false"en<application>, pero úsala solo como parche mientras migras.
Orientación fija en tablets: se acabó
Si tu MainActivity tiene ScreenOrientation = ScreenOrientation.Portrait, presta atención. En pantallas con ancho mínimo de 600 dp (tablets y plegables abiertos), Android 16 ignora screenOrientation, resizeableActivity, minAspectRatio y maxAspectRatio cuando apuntas a API 36. Las excepciones son los juegos y las pantallas pequeñas.
// En un teléfono se respeta; en una tablet con Android 16, se ignora
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true,
ScreenOrientation = ScreenOrientation.Portrait,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
}
Hay una exclusión temporal con una propiedad en el manifiesto:
<application ...>
<!-- Exclusión TEMPORAL: deja de funcionar al apuntar a API 37 -->
<property android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
android:value="true" />
</application>
Pero fíjate en el detalle: según la documentación de Android, esta exclusión no aplica cuando apuntas a API 37, que es justo el valor por defecto de .NET 11. Así que, si vas a migrar a .NET 11 pronto, mejor invierte el tiempo en que tus pantallas funcionen en horizontal.
Permisos: qué revisar
Aquí no hay un gran cambio masivo, pero sí algunos puntos concretos:
✅ Sensores de salud. Si usas BODY_SENSORS (por ejemplo, para leer el ritmo cardíaco), con API 36 se reemplaza por permisos granulares de salud, los mismos de Health Connect. En primer plano se pide READ_HEART_RATE y en segundo plano READ_HEALTH_DATA_IN_BACKGROUND. Además, la app debe declarar una actividad que muestre la política de privacidad, o el permiso se revoca.
<!-- Antes -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Con target API 36 -->
<uses-permission android:name="android.permission.health.READ_HEART_RATE" />
✅ Red local. Android 16 introdujo una protección de red local (sockets, NsdManager, descubrimiento de dispositivos) que se está desplegando en fases. Si tu app habla con impresoras, dispositivos IoT o un servidor en la misma red, pruébala ya:
adb shell am compat enable RESTRICT_LOCAL_NETWORK com.empresa.miapp
adb reboot
Según la documentación, el acceso se recupera concediendo NEARBY_WIFI_DEVICES.
✅ Fotos propias en el selector. Si el usuario da acceso limitado a fotos, las imágenes que creó tu app aparecen preseleccionadas, y si las desmarca pierdes el acceso. Rara vez rompe algo, pero conviene saberlo.
✅ Intents más seguros. Android 16 agrega el atributo intentMatchingFlags (por ahora opcional) para que los componentes solo reciban intents que coincidan con sus filtros. Google dice que en el futuro será el comportamiento por defecto, así que vale la pena probar con enforceIntentFilter.
¿Y las librerías nativas?
Un efecto secundario de actualizar es que, de paso, quedas mejor parado con otro requisito de Google Play: el soporte de páginas de 16 KB. Si vas a tocar el proyecto, revisa también ese punto con mi artículo sobre páginas de 16 KB en Android y .NET MAUI.

¿.NET 10 o directo a .NET 11?
Con .NET 11 a punto de salir, la pregunta es lógica. Mi recomendación:
✅ Ve a .NET 10 si tienes que publicar algo pronto (una corrección, una función comprometida). Es la versión estable con soporte hasta mayo de 2027 y cumple con API 36.
✅ Planea .NET 11 para los próximos meses: te da API 37 por defecto y CoreCLR como runtime, lo que probablemente te deje listo para el requisito de 2027.
❌ Evita saltar directo a .NET 11 con prisa: cambian el runtime, el mínimo de API (24) y los comportamientos de Android 17 a la vez. Son demasiadas variables juntas para una actualización urgente.
Checklist
✅ Tu app está en .NET 10 o superior (net10.0-android → API 36). ✅ Actualizaste Microsoft.Maui.Controls y los NuGet de terceros. ✅ El AndroidManifest.xml no fija un targetSdkVersion viejo (o lo pusiste en 36). ✅ El build no muestra la advertencia XA1006. ✅ Verificaste el targetSdkVersion en el APK o AAB final. ✅ Quitaste windowOptOutEdgeToEdgeEnforcement y revisaste SafeAreaEdges. ✅ No sobrescribes OnBackPressed en MainActivity; usas eventos de ciclo de vida. ✅ Probaste tu app en una tablet o plegable con Android 16. ✅ Cambiaste BODY_SENSORS por permisos de salud (si aplica). ✅ Probaste el acceso a red local (si aplica).
Conclusión
Ya viste:
- Qué exige Google Play: API 36 para actualizaciones y API 35 para seguir visible.
- Que en .NET el target API sale del
TargetFramework, y que .NET 10 es el mínimo para cumplir. - La trampa del
targetSdkVersionen el manifiesto y cómo verificar el paquete final. - Los cambios de Android 16 que más afectan a MAUI: edge-to-edge, predictive back y orientación en pantallas grandes.
- Los permisos que conviene revisar.
Mi recomendación personal: no veas esto como un trámite anual de "subir un número". Cada salto de API trae cambios de comportamiento reales, y la forma más tranquila de vivirlos es actualizar cuando sale cada versión de .NET, no cuando Play Console ya te bloqueó. Y deja el nivel de API explícito en el TFM: te ahorras sorpresas.
¿Ya actualizaste tu app a API 36? ¿Te encontraste con algún cambio de comportamiento que no esperabas? Escríbeme en X (Twitter) y cuéntame.
Nos leemos pronto 😊
Referencias:
- Target API level requirements for Google Play apps (Play Console Help)
- Meet Google Play's target API level requirement (Android Developers)
- Behavior changes: apps targeting Android 16 or higher
- Breaking change: Minimum Android API level raised to 24 (.NET 11)
- .NET for Android build properties
- .NET for Android warning XA1006
- Safe area en .NET MAUI
- What's new in .NET MAUI for .NET 10
- Política de soporte de .NET MAUI
- dotnet/android: Configuration.props y ManifestDocument.cs
- dotnet/maui: MauiAppCompatActivity.cs
