Exoplayer related Composable makes an app not responsive

1 day ago 1
ARTICLE AD BOX

I have a custom PlayerComponent based ExoPlayer. It makes the whole application not responsive when it is playing. I thought that the issue is about too frequent position state updates, but when I made update period longer (1 sec) pretty much nothing changed. I guess the issue might be due to too many events handled in pointerInput lambda. Also I was suggested to substitute Canvas with a Slider, but that doesn't suite my case because I nee to draw more complex timebar in the future. Can you help me out? What has the most sugnificant impact on performance?

@Composable internal fun PlayerControls( modifier: Modifier = Modifier, isPlaying: Boolean, position: Long, duration: Long, onPlay: () -> Unit, onPause: () -> Unit, onSeek: (Long) -> Unit, onSeekBack: () -> Unit, onSeekForward: () -> Unit, isFullScreen: Boolean = false, onFullscreen: ((Boolean) -> Unit)? = null, ) { val coroutineScope = rememberCoroutineScope() var isInteracting by rememberSaveable { mutableStateOf(false) } val interactionSource = remember { MutableInteractionSource() } val controlsShowDuration = 3000 var notActiveTimer by rememberSaveable { mutableIntStateOf(0) } var controlsAlpha by rememberSaveable { mutableFloatStateOf(0f) } val animatedControlsAlpha by animateFloatAsState( targetValue = controlsAlpha, animationSpec = tween(300) ) LaunchedEffect(notActiveTimer) { when (notActiveTimer) { controlsShowDuration -> controlsAlpha = 1f 0 -> controlsAlpha = 0f } } LaunchedEffect(isInteracting) { if (isInteracting) { notActiveTimer = controlsShowDuration } else if (notActiveTimer > 0) { delay(controlsShowDuration.toLong()) notActiveTimer = 0 } } ConstraintLayout( modifier = modifier .clickable( interactionSource = interactionSource, indication = null ) {} .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent(PointerEventPass.Main) val change = event.changes.firstOrNull() ?: continue when { !change.previousPressed && change.pressed -> { // Pointer down isInteracting = true } change.pressed && change.positionChange() != Offset.Zero -> { // Dragging isInteracting = true } change.previousPressed && !change.pressed -> { // Pointer up or Cancelled isInteracting = false } } } } } ) { val (seekBack, seekForward, playBtn, seekBar) = createRefs() /* Left area to seek backward */ /* Right area to seek forward */ if (animatedControlsAlpha > 0) { /* Play/pause button */ if (duration >= 0) { Column( modifier = Modifier .fillMaxWidth() .graphicsLayer(alpha = animatedControlsAlpha) .constrainAs(seekBar) { bottom.linkTo(parent.bottom) start.linkTo(parent.start) end.linkTo(parent.end) } ) { /* Time presentation */ Box( modifier = Modifier .fillMaxWidth() .height(35.dp) .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent(PointerEventPass.Main) val change = event.changes.firstOrNull() ?: continue when { !change.previousPressed && change.pressed -> { // Pointer down isInteracting = true val newProgress = (change.position.x / size.width).coerceIn(0f..1f) val newPosition = (newProgress * duration).toLong() onSeek(newPosition) } change.pressed && change.positionChange() != Offset.Zero -> { // Dragging isInteracting = true val newProgress = (change.position.x / size.width).coerceIn(0f..1f) val newPosition = (newProgress * duration).toLong() onSeek(newPosition) } change.previousPressed && !change.pressed -> { // Pointer up or Cancelled isInteracting = false } } } } } ) { val primaryColor = MaterialTheme.colorScheme.primary Canvas( modifier = Modifier.matchParentSize() ) { val barHeight = 12f val width = size.width val height = size.height val progress = position.toFloat() / duration drawRoundRect( color = Color(200f, 200f, 200f, 0.7f), topLeft = Offset(0f, height / 2f - barHeight * 3f), size = Size(width, barHeight), cornerRadius = CornerRadius(barHeight) ) drawRoundRect( color = primaryColor, topLeft = Offset(0f, height / 2f - barHeight * 3f), size = Size(width * progress, barHeight), cornerRadius = CornerRadius(barHeight) ) drawCircle( color = primaryColor, radius = 1.3f * barHeight, center = Offset( width * progress, height / 2f - barHeight * 3f + barHeight / 2 ) ) } } } } } } }

UPD Left the container and box with a canvas because they are primary suspects because they trigger state changes and throttle performance.

Read Entire Article