From beda44520a0938173550c8806cff8c4e95107090 Mon Sep 17 00:00:00 2001 From: jbibik Date: Thu, 12 Dec 2024 09:20:35 -0800 Subject: [PATCH] [ui-compose] Fix PlayerSurface's use of Player When `PlayerSurface` composable gets a new `Player` object, it should initialise a new Android(Embedded)External Surface with it. However, the lambdas used for that are remembered with the reference to the old Player, even if it has been null'd and released properly. Android(Embedded)ExternalSurface is an interop - `AndroidView` wrapping `SurfaceView` and `TextureView` under the hood. It uses the `onInit` lambda in its factory to create the View and reuses it on later recompositions, making it a longer-lived object than our Player. `RememberUpdatedState` acts makes a mutable State out of the Player and always gets its latest value. One can think of it as creating a reference to the Player object and telling the lambdas to always resolve that reference in the moment, rather than hold onto the Player than was passed-by-value. This change is a precursor to a better lifecycle management of Player and Surface in demo-compose. PiperOrigin-RevId: 705529626 --- .../java/androidx/media3/ui/compose/PlayerSurface.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/PlayerSurface.kt b/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/PlayerSurface.kt index 51f2154970..f681d28025 100644 --- a/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/PlayerSurface.kt +++ b/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/PlayerSurface.kt @@ -24,6 +24,8 @@ import androidx.compose.foundation.AndroidEmbeddedExternalSurface import androidx.compose.foundation.AndroidExternalSurface import androidx.compose.foundation.AndroidExternalSurfaceScope import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi @@ -44,11 +46,16 @@ import androidx.media3.common.util.UnstableApi @UnstableApi @Composable fun PlayerSurface(player: Player, surfaceType: @SurfaceType Int, modifier: Modifier = Modifier) { + // Player might change between compositions, + // we need long-lived surface-related lambdas to always use the latest value + val currentPlayer by rememberUpdatedState(player) val onSurfaceCreated: (Surface) -> Unit = { surface -> - if (player.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) player.setVideoSurface(surface) + if (currentPlayer.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) + player.setVideoSurface(surface) } val onSurfaceDestroyed: () -> Unit = { - if (player.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) player.clearVideoSurface() + if (currentPlayer.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) + player.clearVideoSurface() } val onSurfaceInitialized: AndroidExternalSurfaceScope.() -> Unit = { onSurface { surface, _, _ ->