Introducción: El reproductor "roto" en Android 12 y superior
\nImagine: usted publica con orgullo una actualización de su aplicación reproductora o cliente de podcasts. Pasan unos días y en la sección de reseñas aparecen comentarios furiosos: "¡Después de la actualización la música dejó de reproducirse!", "¡Los botones en la pantalla bloqueada no funcionan!", "¡La aplicación se cierra al intentar reproducir una pista!". ¿Le suena familiar? Lo más probable es que se haya topado con ese error que atormenta a miles de desarrolladores en todo el mundo:
\nMediaSessionCompat: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent\nEste problema es una de las preguntas más populares en Stack Overflow (decenas de miles de visitas) y está directamente relacionado con los cambios en Android 12 (API level 31). La buena noticia: la corrección es simple y le tomará no más de 10 minutos. En este artículo analizaremos las causas del error, la solución correcta en Kotlin y las mejores prácticas para que su aplicación vuelva a funcionar como un reloj.
\n\n¿Por qué ocurre el error? Entendiendo PendingIntent
\nPara entender la esencia del problema, debemos recordar cómo funciona MediaSessionCompat. Este es un componente que permite a su aplicación interactuar con los controles multimedia del sistema: botones del auricular, pantalla de bloqueo, panel de notificaciones. Para ello, crea un PendingIntent, una especie de "llamada diferida" para los servicios del sistema. El sistema usa este Intent para decirle a su aplicación: "El usuario presionó Play/Pause/Next".
A partir de Android 12 (API 31), Google endureció los requisitos de seguridad de PendingIntent. Antes, los desarrolladores podían no especificar la bandera FLAG_IMMUTABLE o FLAG_MUTABLE, y el sistema usaba por defecto la variante mutable. Ahora, usted está obligado a indicar explícitamente si el sistema puede modificar el contenido de su Intent después de su creación. Si no lo hace, la aplicación fallará. Android 12+ simplemente no permitirá crear dicho PendingIntent.
FLAG_IMMUTABLE vs FLAG_MUTABLE: ¿cuál elegir?
\n- \n
- FLAG_IMMUTABLE (recomendado): El Intent no se puede modificar después de su creación. Es más seguro, ya que un atacante no podrá sustituir los datos. Use esta bandera en el 90% de los casos, incluyendo
MediaSessionCompat. \n - FLAG_MUTABLE: El Intent se puede modificar. Solo es necesario en casos especiales, por ejemplo, si usa
NotificationCompatcon acciones que requieren datos dinámicos, o al trabajar conAlarmManager. \n
Para MediaSessionCompat y las notificaciones multimedia, use siempre FLAG_IMMUTABLE. Esto mejora la seguridad y estabilidad de su aplicación.
Corrección paso a paso en Kotlin
\nVeamos cómo se ve el código típico que causa el error y cómo corregirlo. La mayoría de las veces, el problema surge en el método onCreate() de su servicio o navegador multimedia.
Código incorrecto (causará un fallo en Android 12+)
\n// Forma antigua — sin especificar la bandera\nval pendingIntent = PendingIntent.getActivity(\n this,\n 0,\n Intent(this, MainActivity::class.java),\n 0 // <-- ¡Error! No tiene FLAG_IMMUTABLE ni FLAG_MUTABLE\n)\n\nCódigo correcto en Kotlin (corrección)
\nSimplemente agregue PendingIntent.FLAG_IMMUTABLE en el último parámetro:
val pendingIntent = PendingIntent.getActivity(\n this,\n 0,\n Intent(this, MainActivity::class.java),\n PendingIntent.FLAG_IMMUTABLE // <-- ¡Solución!\n)\nSi su aplicación es compatible con versiones anteriores de Android (inferiores a API 31), use una verificación condicional para evitar fallos en dispositivos antiguos:
\nval flags = if (Build.VERS