Jetpack Compose Basics - Ako na prehrávanie videa pomocou knižnice Exoplayer
Mobile
3
 min read
January 18, 2023

Jetpack Compose Basics - Ako na prehrávanie videa pomocou knižnice Exoplayer

Paulina Slaviková
Paulina Slaviková
Android developer
LinkedIn Logo

Jednou z veľmi častých požiadaviek, naprieč rôznymi Android aplikáciami, je prehrávanie videa. Knižnica Exoplayer je jedna z najpopulárnejších knižníc určených pre splnenie tejto úlohy. V tomto článku sa pozrieme na to ako ju použiť a implementovať v Jetpack Compose.

Prečo práve Exoplayer?

Možno niektorí z Vás krútia hlavou nad tým, že potrebujeme použiť na takúto bežnú úlohu nejakú externú knižnicu. Nuž, Android nám síce dáva k dispozícií triedu MediaPlayer, avšak jej možnosti nie sú vo väčšine prípadov postačujúce.

Exoplayer je open-source knižnica od Google, ktorá je na rozdiel od MediaPlayer stabilnejšia, oveľa viac prispôsobiteľná a jej použitie je jednoduchšie.

Jetpack Compose a Exoplayer

Prvý krok, ktorý potrebujeme urobiť je pridanie novej knižnice už do existujúceho projektu. Aktuálne je posledná verzia knižnice 2.18.1 . Najaktuálnejšiu releasovú verziu si môžete skontrolovať tu https://github.com/google/ExoPlayer/releases .


implementation 'com.google.android.exoplayer:exoplayer:2.18.1'

Ďalším krokom je vytvorenie @Composable funkcie, ktorá bude roztiahnutá na celú plochu obrazovky a bude predstavovať priestor pre umiestnenie prehrávača. V tejto composable vytvoríme objekt Exoplayer pomocou volania ExoPlayer.Builder, do ktorého potrebujeme poslať context danej composable. Následne ho dodatočne upravíme:

  • Najdôležitejším krokom je zavolať funkciu setMediaItem, ktorá pomocou funkcie fromUri vytvorí zo stringovej hodnoty videoURL objekt MediaItem, ktorý dokáže prehrávač prehrať. Volanie tejto funkcie vymaže akýkoľvek predošle nastavený playlist a resetne pozíciu prehrávača na pôvodný stav. Nesmieme zabudnúť pridať do súboru manifest toto povolenie <uses-permission android:name="android.permission.INTERNET"/>
  • Nastavením playWhenReady  na true prehrávač spustí video automaticky.
  • Zavolaním prepare funkcie, prehrávač začne načítať médiá a získavať zdroje potrebné na prehrávanie.

@Composable
fun ExoPlayerComp() {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = Color.Black
    ) {
        val videoURL = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"
        val context = LocalContext.current

        val exoPlayer = ExoPlayer.Builder(context)
            .build()
            .apply {
                setMediaItem(fromUri(videoURL))
                playWhenReady = true
                prepare()
           }
    }
}

Keď máme objekt exoplayer správne nastavený, tak musíme vytvoriť nejakú @Composable, ktorá bude obsahovať UI prehrávaného videa a jeho ovládacie prvky. V súčasnosti knižnica Exoplayer ešte nie je prispôsobená pre Compose, no dokážeme si ju prispôsobiť  sami 🙂 a to pomocou @Composable AndroidView, do ktorej vieme vložiť klasické view aké by sme použili v prípade použitia xml, v našom prípade StyledPlayerView.


AndroidView(
    modifier = Modifier.fillMaxSize(),
    factory = {
        StyledPlayerView(context).apply {
           resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
           player = exoPlayer
    }
})

Pomocou funkcie apply vieme tomuto "composed view" nastaviť rovnaké parametre, aké by sme vedeli nastaviť pre view v xml súbore. Podľa parametra resizeMode vieme nastaviť rozpätie videa. V tomto prípade sa veľkosť videa prispôsobuje rozmeru obrazovky so zachovaním pôvodného pomeru strán. Do parametra player vložíme objekt exoplayer, ktorý sme vytvorili v predchádzajúcom kroku. Teraz máme všetko hotové a video môžeme spustiť!

… lenže …

… ak sa vrátite na predchádzajúcu obrazovku, vidíte že video sa stále prehráva na pozadí, aj keď obrazovka už nie je súčasťou kompozície. Je to preto, lebo prehrávač nebol správne releasnutý a neuvoľnil prostriedky, ktoré používal. Tento problém vieme vyriešiť pomocou DisposableEffect efektu, ktorý nám poskytuje Compose API. Pre naše použitie nám postačuje o tomto efekte vedieť len to, že telo efektu sa vykoná vždy keď dôjde ku zmene jeho parametru key1, a že callback metóda onDispose{} sa vykoná vždy, keď composable opustí kompozíciu. A táto funkcia je pre nás kľúčová. Práve v tejto časti vieme zavolať exoPlayer.release().


DisposableEffect(
    key1 = AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = {
            StyledPlayerView(context).apply {
                resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
                player = exoPlayer
            }
         }),
    effect = {
        onDispose {
            exoPlayer.release()
         }
     }
)

Teraz keď sa vrátime na predchádzajúcu obrazovku, video prestane hrať, avšak ak aplikáciu dáme len do pozadia, video sa bude prehrávať naďalej, pretože je stále súčasťou kompozície a callback onDispose{} nebol zavolaný. Na vyriešenie tohto problému potrebujeme získať inštanciu triedy LocalLifecycleOwner, ktorú potrebujeme na počvanie zmien životného cyklu aktivity.


val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)

Následne vytvoríme v DisposableEffect implementáciu LifecycleEventObserver, ktorý prehrávač buď stopne alebo spustí, podľa toho či je aplikácia na popredí alebo v pozadí. Tento observer musíme naviazať na životný cyklus aktivity a takisto ho musíme odstrániť pri opustení kompozície, opäť v callbacku onDispose{}.

Celý kód po úprave je tu 🙂


import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem.fromUri
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
import com.google.android.exoplayer2.ui.StyledPlayerView

@Composable
fun ExoPlayerComp() {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = Color.Black
    ) {
        val videoURL       = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"
        val context        = LocalContext.current
        val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)

        val exoPlayer = ExoPlayer.Builder(context)
            .build()
            .apply {
                setMediaItem(fromUri(videoURL))
                playWhenReady = true
                prepare()
            }

        DisposableEffect(
            key1 = AndroidView(
                modifier = Modifier.fillMaxSize(),
                factory = {
                    StyledPlayerView(context).apply {
                        resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
                        player = exoPlayer
                    }
                }),
            effect = {
                val observer = LifecycleEventObserver { _, event ->
                    when (event) {
                        Lifecycle.Event.ON_RESUME -> {
                            Log.e("LIFECYCLE", "resumed")
                            exoPlayer.play()
                        }
                        Lifecycle.Event.ON_PAUSE  -> {
                            Log.e("LIFECYCLE", "paused")
                            exoPlayer.stop()
                        }
                    }
                }

                val lifecycle = lifecycleOwner.value.lifecycle
                lifecycle.addObserver(observer)

                onDispose {
                    exoPlayer.release()
                    lifecycle.removeObserver(observer)
                }
            }
        )
    }
}

To je všetko. Implementácia nie je úplne triviálna, no dúfam že vám pomôže vo vašom projekte 🙂

Ďalšie články zo série Jetpack Compose Basics:

Zdroje:

Potrebujete pomôct s vaším digitálnym produktom? Kontaktujte nás!
hello@goodrequest.com
Do you need help with your digital product? Contact us!
hello@goodrequest.com

Like what you see?
Join our newsletter.

Great! Welcome to newsletter.
Oops! Something went wrong while submitting your email.
High quality content once a month. No spam, we promise.
Your personal data is processed in accordance with our Memorandum on Personal Data Protection.

Páči sa vám náš content?
Odoberajte newsletter.

Great! Welcome to newsletter.
Oops! Something went wrong while submitting your email.
Vaše osobné údaje sú spracované v súlade s našim Memorandom na ochranu osobných údajov.
Made in Webflow