У меня есть собственный ExoPlayer на основе PlayerComponent. Из-за этого все приложение не реагирует на запросы во время воспроизведения. Я думал, что проблема в слишком частых обновлениях состояния позиции, но когда я увеличил период обновления (1 секунда), практически ничего не изменилось. Я предполагаю, что проблема может быть связана со слишком большим количеством событий, обрабатываемых в лямбда-выражении pointerInput. Также мне предложили заменить Canvas на Slider, но в моем случае это не подходит, поскольку в будущем мне придется рисовать более сложную временную шкалу. Можете ли вы мне помочь? Что оказывает наиболее существенное влияние на производительность?
@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
Оставил контейнер и коробку с холстом, потому что они являются основными подозреваемыми, поскольку вызывают изменения состояния и снижают производительность. UPD 2
Пока я проверял PlayerComponent, я понял, что проблема может заключаться в перекомпоновке AndroidView для поверхности видео
Ранее мой PlayerComponent выглядел так:
Хотя сообщение «Приложение не отвечает» сейчас не появляется, я все еще не уверен на 100%, что проблем с производительностью не возникнет в будущем. Более того, иногда кнопка воспроизведения и панель поиска внутри PlayerControls не отображаются при каждом нажатии.
У меня есть собственный ExoPlayer на основе PlayerComponent. Из-за этого все приложение не реагирует на запросы во время воспроизведения. Я думал, что проблема в слишком частых обновлениях состояния позиции, но когда я увеличил период обновления (1 секунда), практически ничего не изменилось. Я предполагаю, что проблема может быть связана со слишком большим количеством событий, обрабатываемых в лямбда-выражении pointerInput. Также мне предложили заменить Canvas на Slider, но в моем случае это не подходит, поскольку в будущем мне придется рисовать более сложную временную шкалу. Можете ли вы мне помочь? Что оказывает наиболее существенное влияние на производительность? [code]@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 }