Android Academy · Lekcja

Odtwarzanie dźwięku i obrazu

Podstawy odtwarzania multimediów w Androidzie

Lekcja 4 z 413 kroki

Odtwarzanie dźwięku i obrazu to bezpłatna lekcja Android Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Android Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Android Academy zawiera 4 lekcji w sumie.

Poza obrazami: odtwarzanie multimediów

Coil świetnie wyświetla obrazy, ale aplikacje odtwarzają również dźwięk i wideo: dźwięk powiadomienia, podcast, klip w tle czy kanał wideo. Android udostępnia do obsługi multimediów wyspecjalizowane interfejsy API.

Obecnie zalecaną biblioteką jest Media3 (ExoPlayer). Obsługuje transmisję strumieniową, formaty, buforowanie i elementy sterujące odtwarzaniem znacznie lepiej niż starszy MediaPlayer. W tej lekcji odtworzą Państwo prosty dźwięk i wideo w aplikacji Compose.

Dodawanie zależności Media3

Media3 jest podzielone na moduły. W większości aplikacji potrzebne są media3-exoplayer (silnik odtwarzacza) oraz media3-ui (gotowe widoki odtwarzacza). W przypadku wideo w Compose można także użyć PlayerView za pośrednictwem AndroidView.

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

Tworzenie ExoPlayer

Obiekt ExoPlayer tworzy się na podstawie Context. Należy przekazać mu MediaItem (adres URL lub plik lokalny), wywołać prepare(), a następnie rozpocząć odtwarzanie. Odtwarzacz działa we własnych wątkach, więc nie blokuje interfejsu użytkownika.

Co ważne, odtwarzacz zajmuje zasoby systemowe i trzeba go zwolnić — więcej informacji już wkrótce.

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
}

Odtwarzanie dźwięku

W przypadku odtwarzania wyłącznie dźwięku konfiguracja jest identyczna — wystarczy wskazać w MediaItem adres URL pliku audio (mp3, aac itd.). Nie ma widoku do wyświetlenia, więc odtwarzaniem steruje się za pomocą obiektu odtwarzacza: play(), pause(), seekTo().

To wystarczy, aby zbudować odtwarzacz podcastów lub muzyki.

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()
}

Zapamiętywanie odtwarzacza w Compose

W Compose należy utworzyć odtwarzacz tylko raz i zachować go między rekompozycjami. Należy użyć remember, aby nowy odtwarzacz nie był tworzony przy każdej rekompozycji.

Należy połączyć to z prawidłowym czyszczeniem zasobów (w następnej scenie), aby uniknąć wycieków.

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()
        }
    }
}

Zwalnianie odtwarzacza

Odtwarzacz utrzymuje aktywne kodeki, bufory i fokus audio. Jeśli zapomną Państwo go zwolnić, dojdzie do wycieku pamięci, a odtwarzanie może trwać po zamknięciu ekranu. W Compose DisposableEffect wykonuje operacje porządkowe, gdy composable opuszcza kompozycję.

W tym miejscu zawsze należy wywołać player.release().

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
}

Wyświetlanie wideo za pomocą PlayerView

Media3 udostępnia gotowy PlayerView z elementami sterującymi. Compose nie ma natywnego composable do wideo, dlatego widok należy osadzić za pomocą AndroidView i podłączyć do niego odtwarzacz.

Zapewnia to bez dodatkowej pracy przyciski odtwarzania i pauzy, pasek przewijania oraz tryb pełnoekranowy.

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
    )
}

Kompletny ekran wideo

Połączmy teraz wszystkie elementy: utwórzmy odtwarzacz, zapamiętajmy go, podłączmy do PlayerView i zwolnijmy podczas usuwania. Otrzymamy samodzielny ekran wideo, który można ponownie wykorzystać.

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)
    )
}

Śledzenie stanu odtwarzania

Aby aktualizować interfejs użytkownika (ikonę odtwarzania/pauzy lub pasek postępu), należy nasłuchiwać odtwarzacza za pomocą Player.Listener. Zgłasza on zmiany stanu, takie jak buforowanie, gotowość i zakończenie, a także informuje, czy odtwarzanie jest aktywne.

Listener należy dodać, gdy ekran się pojawia, i usunąć podczas jego usuwania.

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
}

Wstrzymywanie po przejściu aplikacji do tła

W przypadku wideo osadzonego w interfejsie (a nie aplikacji muzycznej) zwykle należy wstrzymać odtwarzanie, gdy użytkownik opuszcza ekran, i wznowić je po powrocie. Można powiązać odtwarzanie z cyklem życia Compose, obserwując Lifecycle i wstrzymując odtwarzanie przy ON_PAUSE.

Zapobiega to odtwarzaniu wideo, którego użytkownik nie może oglądać, i oszczędza baterię.

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) }
    }
}

Dźwięk w tle z MediaSession

W przypadku dźwięku, który powinien być odtwarzany po przejściu aplikacji do tła (muzyka, podcasty), należy umieścić odtwarzanie w MediaSessionService. Integruje to odtwarzanie z elementami sterującymi systemu, ekranem blokady i przyciskami Bluetooth.

Wymaga to nieco więcej pracy, ale główna idea polega na udostępnieniu odtwarzacza za pośrednictwem 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()
    }
}

Szybkie sprawdzenie

Na ekranie Compose, który przechowuje obiekt ExoPlayer, którego API Compose należy użyć, aby zwolnić odtwarzacz, gdy ekran opuszcza kompozycję?

Podsumowanie

Dodali Państwo odtwarzanie dźwięku i wideo:

  • Należy używać Media3 ExoPlayer, nowoczesnego następcy MediaPlayer.
  • Należy utworzyć odtwarzacz, ustawić MediaItem, wywołać prepare(), a następnie play().
  • W Compose odtwarzacz należy zapamiętać za pomocą remember i zwolnić w onDispose elementu DisposableEffect.
  • Wideo należy wyświetlać, osadzając PlayerView za pomocą AndroidView.
  • Stan należy śledzić za pomocą Player.Listener, a do odtwarzania dźwięku w tle używać MediaSessionService.

To kończy kurs Images & Media Loading with Coil. Potrafią już Państwo płynnie ładować obrazy i odtwarzać multimedia w nowoczesny sposób na Androidzie.

Bezpłatny start

Ucz się Kotlin dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
36
Lekcje
152

Często zadawane pytania

Czy lekcja „Odtwarzanie dźwięku i obrazu” jest bezpłatna?

Tak — pełny tekst „Odtwarzanie dźwięku i obrazu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Android Academy, przejdź na CoddyKit PRO. Kurs Android Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Odtwarzanie dźwięku i obrazu”?

Podstawy odtwarzania multimediów w Androidzie Ćwiczysz Android Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Android Academy?

Nie wymagamy żadnego doświadczenia. Android Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Odtwarzanie dźwięku i obrazu”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Android Academy?

Tak. Każda lekcja Android Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wczytywanie obrazów za pomocą Coil
  2. Elementy zastępcze i stany błędów
  3. Buforowanie i wydajność
  4. Odtwarzanie dźwięku i obrazu
← Powrót do Android Academy