Ошибка PendingIntent: FLAG_IMMUTABLE или FLAG_MUTABLE обязательны для Android 12+
Решение проблемы: добавляем флаги FLAG_IMMUTABLE или FLAG_MUTABLE
Ошибка возникает из-за изменений в Android 12 (API 31): все PendingIntent должны явно указывать свою изменяемость. Это повышает безопасность приложений. Вот как правильно исправить ваш код:
1. Для обычных Intent (Activity, Service)
В большинстве случаев используйте FLAG_IMMUTABLE — он безопаснее и рекомендуется Google:
val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
2. Для MediaSession и уведомлений
Особенно важно правильно настроить PendingIntent для медиа-сессии. Вот полный пример для MediaSessionCompat:
// Создаём Intent для медиа-кнопок
val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON)
mediaButtonIntent.component = ComponentName(this, MediaButtonReceiver::class.java)
val mediaPendingIntent = PendingIntent.getBroadcast(
this,
0,
mediaButtonIntent,
PendingIntent.FLAG_IMMUTABLE
)
// Настраиваем MediaSession
val mediaSession = MediaSessionCompat(this, "MyMediaSession")
mediaSession.setMediaButtonReceiver(mediaPendingIntent)
// Для уведомления (Notification) также используем FLAG_IMMUTABLE
val playAction = NotificationCompat.Action(
R.drawable.ic_play,
"Play",
mediaPendingIntent
)
3. Для MediaStyle уведомления
Если используете MediaStyle, добавьте setPendingIntentShowUi:
val contentIntent = PendingIntent.getActivity(
this,
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setStyle(androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.sessionToken)
.setShowActionsInCompactView(0, 1, 2))
.setContentIntent(contentIntent)
.build()
4. Когда использовать FLAG_MUTABLE?
FLAG_MUTABLE нужен только в редких случаях, когда вы планируете изменять параметры Intent после создания PendingIntent (например, для динамических данных). Для медиа-уведомлений он почти никогда не требуется.
5. Важные моменты для Android 12+
- Все PendingIntent в вашем приложении должны иметь флаг — не только для медиа.
- Если используете
AlarmManager,NotificationManagerилиJobScheduler— проверьте их PendingIntent. - Для
BroadcastReceiverс неявными Intent (например,ACTION_MEDIA_BUTTON) обязательно используйтеFLAG_IMMUTABLE.
6. Полный рабочий пример
class MyMediaPlayerService : Service() {
private lateinit var mediaSession: MediaSessionCompat
override fun onCreate() {
super.onCreate()
// Создаём PendingIntent для кнопок
val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON).apply {
component = ComponentName(this@MyMediaPlayerService, MediaButtonReceiver::class.java)
}
val mediaPendingIntent = PendingIntent.getBroadcast(
this, 0, mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE
)
// Создаём MediaSession
mediaSession = MediaSessionCompat(this, "MyService").apply {
setMediaButtonReceiver(mediaPendingIntent)
isActive = true
}
// Создаём уведомление
val notification = createNotification(mediaPendingIntent)
startForeground(NOTIFICATION_ID, notification)
}
private fun createNotification(mediaPendingIntent: PendingIntent): Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("My Media Player")
.setContentText("Playing...")
.addAction(R.drawable.ic_play, "Play", mediaPendingIntent)
.setStyle(androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.sessionToken))
.build()
}
}
После добавления флагов ошибка исчезнет. Рекомендую проверить весь проект на наличие других PendingIntent без флагов — Android Studio подсвечивает такие места.
Альтернативное решение: использование MediaSessionCompat.setMediaButtonReceiver()
Если вы используете MediaSessionCompat с MediaButtonReceiver, можно упростить код, используя готовый метод, который сам создаёт правильный PendingIntent:
// Вместо ручного создания PendingIntent для медиа-кнопок
val mediaSession = MediaSessionCompat(this, "MySession")
// Используем встроенный метод (он сам добавит правильные флаги)
mediaSession.setMediaButtonReceiver(
PendingIntent.getBroadcast(
this,
0,
Intent(Intent.ACTION_MEDIA_BUTTON).apply {
component = ComponentName(this, MediaButtonReceiver::class.java)
},
PendingIntent.FLAG_IMMUTABLE
)
)
Дополнительная рекомендация
Для уведомлений с MediaStyle можно использовать NotificationCompat.MediaStyle.setMediaSession(), который автоматически связывает уведомление с сессией, и вам не нужно создавать отдельный PendingIntent для кнопок — система сама обработает нажатия через MediaSession.
Также проверьте, что ваш AndroidManifest.xml содержит правильную декларацию MediaButtonReceiver:
<receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Этот подход минимизирует ручное создание PendingIntent и снижает риск ошибок с флагами.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги