diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt --- a/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt +++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt @@ -26,7 +26,7 @@ class PlaybackServiceBinder(val service: ExpoVideoPlaybackService) : Binder() @OptIn(UnstableApi::class) class ExpoVideoPlaybackService : MediaSessionService() { - private val mediaSessions = mutableMapOf() + private val mediaSessions = java.util.concurrent.ConcurrentHashMap() private val binder = PlaybackServiceBinder(this) private val commandSeekForward = SessionCommand(SEEK_FORWARD_COMMAND, Bundle.EMPTY) @@ -136,10 +136,17 @@ class ExpoVideoPlaybackService : MediaSessionService() { private fun cleanup() { hideAllNotifications() - mediaSessions.forEach { (_, session) -> + // Kopia przed iteracja: session.release() wola z powrotem unregisterPlayer, ktory + // usuwa wpis z mediaSessions w trakcie forEach -> ConcurrentModificationException + // przy zamykaniu apki z listy ostatnich (Goon, Sentry 147164135, 2026-09-15). + // Mapa jest ConcurrentHashMap (patrz deklaracja), bo sama kopia tez iteruje mape, + // a wpisy znikaja z innego watku przy zwalnianiu playera: na LinkedHashMap `toList()` + // wywalalo sie wtedy tak samo (Goon 0.4.1, Sentry GOON-6W, 2026-09-17). + val sessions = mediaSessions.values.toList() + mediaSessions.clear() + sessions.forEach { session -> session.release() } - mediaSessions.clear() } private fun hidePlayerNotification(player: ExoPlayer) {