0Pricing
Android Academy · Lección

Reproducción de audio y vídeo

Reproducción multimedia sencilla en Android

Reproducción de audio y vídeo es una lección gratuita de Android Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Android Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Android Academy incluye 4 lecciones en total.

Más allá de las imágenes: reproducción multimedia

Coil muestra imágenes de forma excelente, pero las aplicaciones también reproducen sonido y vídeo: un tono de notificación, un pódcast, un clip en segundo plano o un feed de vídeo. Android cuenta con API específicas para contenido multimedia.

La biblioteca moderna recomendada es Media3 (ExoPlayer). Gestiona la transmisión, los formatos, el almacenamiento en búfer y los controles de reproducción mucho mejor que el antiguo MediaPlayer. En esta lección reproducirá audio y vídeo sencillos en una aplicación de Compose.

Añadir dependencias de Media3

Media3 está dividida en módulos. Para la mayoría de las aplicaciones necesitará media3-exoplayer (el motor del reproductor) y media3-ui (vistas de reproductor listas para usar). Para vídeo en Compose también puede usar PlayerView mediante AndroidView.

// build.gradle.kts (module level)
dependencies {
    implementation("androidx.media3:media3-exoplayer:1.5.0")
    implementation("androidx.media3:media3-ui:1.5.0")
}

Crear un ExoPlayer

Un ExoPlayer se crea a partir de un Context. Se le proporciona un MediaItem (una URL o un archivo local), se llama a prepare() y se inicia la reproducción. Se ejecuta en sus propios hilos, por lo que no bloquea la interfaz.

Es importante tener en cuenta que un reproductor mantiene recursos del sistema y debe liberarse; hablaremos de ello enseguida.

import android.content.Context
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

fun createPlayer(context: Context, url: String): ExoPlayer {
    val player = ExoPlayer.Builder(context).build()
    player.setMediaItem(MediaItem.fromUri(url))
    player.prepare()
    player.playWhenReady = true
    return player
}

Reproducir audio

Para reproducir solo audio, la configuración es idéntica; basta con apuntar MediaItem a una URL de audio (mp3, aac, etc.). No hay ninguna vista que mostrar, por lo que se controla mediante el objeto reproductor: play(), pause(), seekTo().

Esto es suficiente para crear un reproductor de pódcast o música.

import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

fun playPodcast(player: ExoPlayer, audioUrl: String) {
    player.setMediaItem(MediaItem.fromUri(audioUrl))
    player.prepare()
    player.play()
}

fun togglePause(player: ExoPlayer) {
    if (player.isPlaying) player.pause() else player.play()
}

Recordar el reproductor en Compose

En Compose debe crear el reproductor una sola vez y conservarlo entre recomposiciones. Use remember para evitar crear un reproductor nuevo en cada recomposición.

Combine esto con una limpieza adecuada (en la siguiente escena) para no provocar fugas de recursos.

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

@Composable
fun rememberPlayer(url: String): ExoPlayer {
    val context = LocalContext.current
    return remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(url))
            prepare()
        }
    }
}

Liberar el reproductor

Un reproductor mantiene activos los códecs, los búferes y el foco de audio. Si olvida liberarlo, provoca una fuga de memoria y puede hacer que la reproducción continúe después de que desaparezca la pantalla. En Compose, DisposableEffect ejecuta la limpieza cuando el composable sale de la composición.

Llame siempre a player.release() allí.

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.media3.exoplayer.ExoPlayer

@Composable
fun ManagedPlayer(player: ExoPlayer) {
    DisposableEffect(player) {
        onDispose {
            player.release()
        }
    }
    // ... use player here
}

Mostrar vídeo con PlayerView

Media3 proporciona un PlayerView listo para usar con controles. Compose no tiene un composable de vídeo nativo, por lo que debe insertar la vista mediante AndroidView y asociarle el reproductor.

Así obtiene controles de reproducción y pausa, una barra de búsqueda y pantalla completa sin trabajo adicional.

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView

@Composable
fun VideoPlayer(player: ExoPlayer, modifier: Modifier = Modifier) {
    AndroidView(
        factory = { context ->
            PlayerView(context).apply {
                this.player = player
            }
        },
        modifier = modifier
    )
}

Una pantalla de vídeo completa

Vamos a unirlo todo: cree el reproductor, recuérdelo, asígnelo a un PlayerView y libérelo al eliminarlo. Esta es una pantalla de vídeo independiente que puede reutilizar.

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView

@Composable
fun VideoScreen(videoUrl: String) {
    val context = LocalContext.current
    val player = remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(videoUrl))
            prepare()
            playWhenReady = true
        }
    }
    DisposableEffect(Unit) { onDispose { player.release() } }

    AndroidView(
        factory = { PlayerView(it).apply { this.player = player } },
        modifier = Modifier.fillMaxWidth().height(220.dp)
    )
}

Seguir el estado de reproducción

Para actualizar la interfaz (un icono de reproducción/pausa o una barra de progreso), escuche al reproductor con un Player.Listener. Este informa de cambios de estado, como almacenamiento en búfer, listo y finalizado, además de indicar si la reproducción está activa.

Añada el listener cuando aparezca la pantalla y elimínelo al eliminarla.

import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer

fun observe(player: ExoPlayer, onPlaying: (Boolean) -> Unit): Player.Listener {
    val listener = object : Player.Listener {
        override fun onIsPlayingChanged(isPlaying: Boolean) {
            onPlaying(isPlaying)
        }
        override fun onPlaybackStateChanged(state: Int) {
            when (state) {
                Player.STATE_BUFFERING -> { /* show spinner */ }
                Player.STATE_READY -> { /* ready */ }
                Player.STATE_ENDED -> { /* finished */ }
            }
        }
    }
    player.addListener(listener)
    return listener
}

Pausar cuando la aplicación pasa a segundo plano

Para vídeo integrado (no una aplicación de música), normalmente querrá pausarlo cuando el usuario abandone la pantalla y reanudarlo cuando vuelva. Puede vincular la reproducción al ciclo de vida de Compose observando Lifecycle y pausando en ON_PAUSE.

Así evita reproducir un vídeo que el usuario no puede ver y ahorra batería.

import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.media3.exoplayer.ExoPlayer

@Composable
fun PauseWithLifecycle(player: ExoPlayer) {
    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> player.pause()
                Lifecycle.Event.ON_RESUME -> player.play()
                else -> {}
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
    }
}

Audio en segundo plano con MediaSession

Para el audio que debe seguir reproduciéndose cuando la aplicación pasa a segundo plano (música o pódcasts), incluya la reproducción en un MediaSessionService. Esto se integra con los controles del sistema, la pantalla de bloqueo y los botones Bluetooth.

Es algo más complejo, pero la idea principal es exponer el reproductor mediante una MediaSession.

import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService

class PlaybackService : MediaSessionService() {
    private var session: MediaSession? = null

    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        session = MediaSession.Builder(this, player).build()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = session

    override fun onDestroy() {
        session?.run { player.release(); release() }
        session = null
        super.onDestroy()
    }
}

Comprobación rápida

En una pantalla de Compose que contiene un ExoPlayer, ¿qué API de Compose debe usar para liberar el reproductor cuando la pantalla sale de la composición?

Resumen

Ha añadido reproducción de audio y vídeo:

  • Use Media3 ExoPlayer, el sustituto moderno de MediaPlayer.
  • Cree un reproductor, establezca un MediaItem, llame a prepare() y después a play().
  • En Compose, recuerde el reproductor con remember y libérelo en DisposableEffect, dentro de onDispose.
  • Muestre vídeo insertando PlayerView con AndroidView.
  • Siga el estado con un Player.Listener; use un MediaSessionService para el audio en segundo plano.

Con esto completa el curso Carga de imágenes y contenido multimedia con Coil. Ahora puede cargar imágenes fluidamente y reproducir contenido multimedia de la forma moderna en Android.

Preguntas frecuentes

¿La lección «Reproducción de audio y vídeo» es gratis?

Sí — el texto completo de «Reproducción de audio y vídeo» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Android Academy, actualiza a CoddyKit PRO. El curso de Android Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Reproducción de audio y vídeo»?

Reproducción multimedia sencilla en Android Practicas Android Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Android Academy?

No se requiere experiencia previa. Android Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Reproducción de audio y vídeo»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Android Academy?

Sí. Cada lección de Android Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Carga de imágenes con Coil
  2. Marcadores de posición y estados de error
  3. Caché y rendimiento
  4. Reproducción de audio y vídeo
← Volver a Android Academy