reactormonk
09/20/2023, 3:31 PMvar done by remember(mediaItems) { mutableStateOf(false) }
<inside callback>
done = true
doesn't trigger a recompose? the done = true
is getting invoked, println debugging.vide
09/20/2023, 3:46 PMdone
inreactormonk
09/20/2023, 3:47 PMBox(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
if (!done) {
VideoPlayer(exoPlayer)
} else {
ErrorTemplate(message = stringResource(R.string.end_of_playlist))
}
}
And I'm wondering why the ErrorTemplate
isn't being displayedvide
09/20/2023, 3:47 PMmediaItems
?mediaItems
changesreactormonk
09/20/2023, 3:48 PMremember
block, no recreationvide
09/20/2023, 3:49 PMreactormonk
09/20/2023, 3:51 PMvar done by remember(mediaItems) { Log.d(TAG, "Recreating done."); mutableStateOf(false) }
var started = false
val listener = object : Player.Listener {
override fun onEvents(player: Player, events: Player.Events) {
if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
if (player.playbackState == Player.STATE_ENDED && started) {
done = true
Log.d(TAG, "Done.")
}
}
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
if (isPlaying) {
started = true
}
}
}
val exoPlayer = remember(context) {
val mediaSourceFactory = DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(
DefaultLoadErrorHandlingPolicy()
)
val player = ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build()
player.addListener(listener)
// player.addAnalyticsListener(EventLogger())
player.repeatMode = REPEAT_MODE_OFF
player
}
if (exoPlayer.availableCommands.contains(Player.COMMAND_CHANGE_MEDIA_ITEMS)) {
exoPlayer.setMediaItems(mediaItems)
exoPlayer.prepare()
exoPlayer.playWhenReady = true
} else {
Log.e(TAG, "Can't change media items!?!")
}
if (mediaItemIndex > -1) {
exoPlayer.seekTo(mediaItemIndex, position)
}
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
if (!done) {
VideoPlayer(exoPlayer)
} else {
ErrorTemplate(message = stringResource(R.string.end_of_playlist))
}
}
... and a bit more lifecycle stuff to clean up things.vide
09/20/2023, 3:56 PMexoPlayer.setMediaItems(mediaItems)
exoPlayer.prepare()
exoPlayer.seekTo(mediaItemIndex, position)
on every recompositionstarted
and a new listener
on every recompositionreactormonk
09/20/2023, 3:58 PMremember
blocksvide
09/20/2023, 3:58 PMstarted
update correctly because the player is memoized in the remember block for creating the exoplayer instance 🤔reactormonk
09/20/2023, 3:59 PMUnit
as return?vide
09/20/2023, 3:59 PMreactormonk
09/20/2023, 4:00 PMremember(mediaItems) {
if (exoPlayer.availableCommands.contains(Player.COMMAND_CHANGE_MEDIA_ITEMS)) {
exoPlayer.setMediaItems(mediaItems)
exoPlayer.prepare()
exoPlayer.playWhenReady = true
} else {
Log.e(TAG, "Can't change media items!?!")
}
}
vide
09/20/2023, 4:02 PMLaunchedEffect
here? https://developer.android.com/jetpack/compose/side-effects#launchedeffectreactormonk
09/20/2023, 4:02 PMvide
09/20/2023, 4:14 PMreactormonk
09/20/2023, 4:14 PMMutableState
from non-main-threadsvide
09/20/2023, 4:23 PMreactormonk
09/20/2023, 4:24 PMMediaItems: []
Recreating done.
Exo player: androidx.media3.exoplayer.ExoPlayerImpl@fef39da
currentMediaItem: null
Done: false
MediaItems: [androidx.media3.common.MediaItem@1db4b9bf]
Recreating done.
Exo player: androidx.media3.exoplayer.ExoPlayerImpl@fef39da
currentMediaItem: null
Done: false
Done.
Recreating done.
is fine, I'm changing the MediaItems, so it's expected. The final Done.
is where I'm changing the done
variable.vide
09/20/2023, 4:27 PMmediaItems
. That is a problem, can you see why?reactormonk
09/20/2023, 4:28 PMvar started = false
LaunchedEffect(mediaItems) {
started = false
}
vide
09/20/2023, 4:30 PMlistener
contains a reference to the first done
state. You change mediaItems
after it has captured it in the closure, and it creates a new state, which always stays in false
. Because your listener is actually updating the first instance.reactormonk
09/20/2023, 4:32 PMDisposableEffect
to change out the listenervide
09/20/2023, 4:34 PMreactormonk
09/20/2023, 4:34 PMdata class
?Stylianos Gakis
09/20/2023, 4:35 PMrememberPagerState
anywhere in your app? PagerState
is an example of such a classvide
09/20/2023, 4:36 PMdata
, but it can also be that. You can just use a regular class too. The main point is to separate your state and mutation logic from the UI codeStylianos Gakis
09/20/2023, 4:36 PMreactormonk
09/20/2023, 4:37 PMLaunchedEffect
and recomposing, had similar bugs in other parts of the code 😅vide
09/20/2023, 4:44 PMFilip Wiesner
09/20/2023, 7:02 PMAh, so you are changing yourI love this ❤️ I wish someone gave me code reviews like this ☺️. That is a problem, can you see why?mediaItems