Stosy sieciowe

ExoPlayer jest często używany do przesyłania strumieniowego multimediów przez internet. Obsługuje wiele stosów sieciowych do wysyłania podstawowych żądań sieciowych. Wybór stosu sieciowego może mieć znaczący wpływ na wydajność strumieniowania.

Na tej stronie znajdziesz informacje o tym, jak skonfigurować ExoPlayer do korzystania z wybranego stosu sieciowego, listę dostępnych opcji, wskazówki dotyczące wyboru stosu sieciowego dla aplikacji oraz wyjaśnienie, jak włączyć buforowanie przesyłanych strumieniowo multimediów.

Konfigurowanie ExoPlayera do korzystania z określonego stosu sieciowego

ExoPlayer wczytuje dane za pomocą komponentów DataSource, które pobiera z instancji DataSource.Factory wstrzykiwanych z kodu aplikacji.

Jeśli Twoja aplikacja musi tylko odtwarzać treści http(s), wybór stosu sieciowego jest tak prosty, jak zaktualizowanie wszystkich instancji DataSource.Factory, które aplikacja wstawia, tak aby były instancjami HttpDataSource.Factory odpowiadającymi stosowi sieciowemu, którego chcesz używać. Jeśli Twoja aplikacja musi też odtwarzać treści inne niż http(s), np. pliki lokalne, użyj tego kodu:DefaultDataSource.Factory

Kotlin

DefaultDataSource.Factory(
  ...
  /* baseDataSourceFactory= */ PreferredHttpDataSource.Factory(...))

Java

new DefaultDataSource.Factory(
    ...
    /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(...));

W tym przykładzie PreferredHttpDataSource.Factory to fabryka odpowiadająca preferowanemu stosowi sieci. Warstwa DefaultDataSource.Factory dodaje obsługę źródeł innych niż http(s), takich jak pliki lokalne.

Poniższy przykład pokazuje, jak utworzyć ExoPlayer, który będzie korzystać ze stosu sieciowego Cronet i obsługiwać odtwarzanie treści innych niż http(s).

Kotlin

// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
val cronetDataSourceFactory = CronetDataSource.Factory(cronetEngine, executor)

// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (such as files, resources,
// etc).
val dataSourceFactory =
  DefaultDataSource.Factory(context, /* baseDataSourceFactory= */ cronetDataSourceFactory)

// Inject the DefaultDataSource.Factory when creating the player.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
CronetDataSource.Factory cronetDataSourceFactory =
    new CronetDataSource.Factory(cronetEngine, executor);

// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (such as files, resources,
// etc).
DefaultDataSource.Factory dataSourceFactory =
    new DefaultDataSource.Factory(
        context, /* baseDataSourceFactory= */ cronetDataSourceFactory);

// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

Obsługiwane stosy sieciowe

ExoPlayer obsługuje bezpośrednio HttpEngine, Cronet, OkHttp i wbudowany w Androida domyślny stos sieciowy. ExoPlayer można też rozszerzyć, aby obsługiwał dowolny inny stos sieciowy działający na Androidzie.

HttpEngine

HttpEngine to zalecany domyślny stos sieciowy na Androidzie od interfejsu API 34 (lub rozszerzeń S 7). W większości przypadków wewnętrznie korzysta ze stosu sieciowego Cronet, który obsługuje protokoły HTTP, HTTP/2 i HTTP/3 przez QUIC.

ExoPlayer obsługuje HttpEngine za pomocą HttpEngineDataSource.Factory. Fabrykę źródła danych możesz wstrzyknąć w sposób opisany w artykule Konfigurowanie ExoPlayera do korzystania z określonego stosu sieciowego.

Cronet

Cronet to stos sieciowy Chromium udostępniany aplikacjom na Androida jako biblioteka. Cronet korzysta z wielu technologii, które zmniejszają czas oczekiwania i zwiększają przepustowość żądań sieciowych potrzebnych do działania aplikacji, w tym żądań wysyłanych przez ExoPlayer. Obsługuje natywnie protokoły HTTP, HTTP/2 i HTTP/3 over QUIC. Cronet jest używany przez niektóre z największych aplikacji do streamingu na świecie, w tym YouTube.

ExoPlayer obsługuje Cronet za pomocą biblioteki Cronet. Szczegółowe instrukcje korzystania z niej znajdziesz w README.mdbibliotece. Pamiętaj, że biblioteka Cronet może korzystać z 3 implementacji Cronet:

  1. Usługi Google Play: zalecamy korzystanie z tej implementacji w większości przypadków i przełączanie się na wbudowany stos sieciowy Androida (DefaultHttpDataSource), jeśli Usługi Google Play są niedostępne.
  2. Cronet Embedded: może być dobrym wyborem, jeśli duży odsetek użytkowników znajduje się na rynkach, na których Usługi Google Play nie są powszechnie dostępne, lub jeśli chcesz kontrolować dokładną wersję używanej implementacji Croneta. Główną wadą Cronet Embedded jest to, że dodaje do aplikacji około 8 MB.
  3. Cronet Fallback: implementacja rezerwowa Cronet wdraża interfejs API Cronet jako otokę wbudowanego stosu sieciowego Androida. Nie należy go używać z ExoPlayerem, ponieważ bezpośrednie korzystanie z wbudowanego stosu sieciowego Androida (za pomocą DefaultHttpDataSource) jest bardziej wydajne.

OkHttp

OkHttp to kolejny nowoczesny stos sieciowy, który jest powszechnie używany w wielu popularnych aplikacjach na Androida. Obsługuje protokoły HTTP i HTTP/2, ale nie obsługuje jeszcze HTTP/3 przez QUIC.

ExoPlayer obsługuje OkHttp za pomocą biblioteki OkHttp. Szczegółowe instrukcje korzystania z niej znajdziesz w README.mdbibliotece. W przypadku korzystania z biblioteki OkHttp stos sieciowy jest osadzony w aplikacji. Jest to podobne do Cronet Embedded, ale OkHttp jest znacznie mniejszy i zajmuje mniej niż 1 MB.

wbudowany stos sieciowy Androida,

ExoPlayer obsługuje korzystanie z wbudowanego stosu sieciowego Androida z DefaultHttpDataSourceDefaultHttpDataSource.Factory, które są częścią podstawowej biblioteki ExoPlayer.

Dokładna implementacja stosu sieciowego zależy od oprogramowania działającego na urządzeniu. Na większości urządzeń obsługiwany jest tylko protokół HTTP (czyli HTTP/2 i HTTP/3 przez QUIC nie są obsługiwane).

Inne stosy sieciowe

Aplikacje mogą też integrować z ExoPlayerem inne stosy sieciowe. Aby to zrobić, zaimplementuj HttpDataSource, który otacza stos sieciowy, oraz odpowiedni HttpDataSource.Factory. Biblioteki Cronet i OkHttp ExoPlayera to dobre przykłady tego, jak to zrobić.

W przypadku integracji z czystym stosem sieciowym Java warto zaimplementować DataSourceContractTest, aby sprawdzić, czy HttpDataSource implementacjaHttpDataSource działa prawidłowo. OkHttpDataSourceContractTest w bibliotece OkHttp to dobry przykład, jak to zrobić.

Wybór stosu sieciowego

W tabeli poniżej znajdziesz zalety i wady stosów sieciowych obsługiwanych przez ExoPlayera.

Stos sieciowy Protokoły Wpływ rozmiaru pliku APK Uwagi
HttpEngine HTTP
HTTP/2
HTTP/3 przez QUIC
Brak Dostępne tylko w przypadku interfejsu API 34 lub rozszerzeń S 7
Cronet (Usługi Google Play) HTTP
HTTP/2
HTTP/3 przez QUIC
Mały
(<100 KB)
Wymaga Usług Google Play. Wersja Cronet jest aktualizowana automatycznie
Cronet (wbudowany) HTTP
HTTP/2
HTTP/3 przez QUIC
Duży
(ok. 8 MB)
Wersja Croneta kontrolowana przez dewelopera aplikacji
Cronet (wartość zastępcza) HTTP
(zależy od urządzenia)
Mały
(<100 KB)
Niezalecane w przypadku ExoPlayera
OkHttp HTTP
HTTP/2
Mały
(<1 MB)
Wbudowany stos sieciowy HTTP
(zależy od urządzenia)
Brak Implementacja różni się w zależności od urządzenia

Protokoły HTTP/2 i HTTP/3 over QUIC mogą znacznie poprawić wydajność przesyłania strumieniowego multimediów. W szczególności w przypadku przesyłania strumieniowego adaptacyjnych multimediów rozpowszechnianych za pomocą sieci dostarczania treści (CDN) istnieją przypadki, w których użycie tych protokołów może znacznie zwiększyć wydajność sieci CDN. Z tego powodu obsługa protokołów HTTP/2 i HTTP/3 przez HttpEngine i Cronet za pomocą QUIC (oraz obsługa protokołu HTTP/2 przez OkHttp) jest dużą zaletą w porównaniu z wbudowanym stosem sieciowym Androida, pod warunkiem że serwery, na których hostowane są treści, również obsługują te protokoły.

W przypadku strumieniowania multimediów zalecamy korzystanie z HttpEngine lub Cronet udostępnianych przez Usługi Google Play. Jeśli Usługi Google Play są niedostępne, należy użyć DefaultHttpDataSource. To zalecenie zapewnia dobrą równowagę między umożliwieniem korzystania z protokołów HTTP/2 i HTTP/3 za pomocą QUIC na większości urządzeń a unikaniem znacznego zwiększenia rozmiaru pliku APK. Od tej rekomendacji są wyjątki. W przypadku, gdy usługi Google Play prawdopodobnie nie będą dostępne na znacznej części urządzeń, na których będzie działać Twoja aplikacja, bardziej odpowiednie może być użycie Cronet Embedded lub OkHttp. Korzystanie z wbudowanego stosu sieciowego może być dopuszczalne, jeśli rozmiar pliku APK jest kluczową kwestią lub jeśli przesyłanie strumieniowe multimediów jest tylko niewielką częścią funkcjonalności aplikacji.

Oprócz mediów warto wybrać jeden stos sieciowy dla wszystkich operacji sieciowych wykonywanych przez aplikację. Umożliwia to efektywne grupowanie i udostępnianie zasobów (takich jak gniazda) między ExoPlayerem a innymi komponentami aplikacji.

Ponieważ aplikacja najprawdopodobniej będzie musiała wykonywać operacje sieciowe niezwiązane z odtwarzaniem multimediów, wybór stosu sieciowego powinien ostatecznie uwzględniać nasze powyższe zalecenia dotyczące strumieniowania multimediów w izolacji, wymagania innych komponentów, które wykonują operacje sieciowe, oraz ich względne znaczenie dla aplikacji.

Buforowanie multimediów

ExoPlayer obsługuje buforowanie wczytanych bajtów na dysku, aby zapobiec wielokrotnemu wczytywaniu tych samych bajtów z sieci. Jest to przydatne, gdy chcesz cofnąć odtwarzanie bieżącego pliku lub powtórzyć ten sam element.

Pamięć podręczna wymaga instancji SimpleCache wskazującej dedykowany katalog pamięci podręcznej i CacheDataSource.Factory:

Kotlin

// Note: This should be a singleton in your app.
val databaseProvider = StandaloneDatabaseProvider(context)

// An on-the-fly cache should evict media when reaching a maximum disk space limit.
val cache =
    SimpleCache(
        downloadDirectory, LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider)

// Configure the DataSource.Factory with the cache and factory for the desired HTTP stack.
val cacheDataSourceFactory =
    CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory)

// Inject the DefaultDataSource.Factory when creating the player.
val player =
    ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory))
        .build()

Java

// Note: This should be a singleton in your app.
DatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context);

// An on-the-fly cache should evict media when reaching a maximum disk space limit.
Cache cache =
    new SimpleCache(
        downloadDirectory, new LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider);

// Configure the DataSource.Factory with the cache and factory for the desired HTTP stack.
DataSource.Factory cacheDataSourceFactory =
    new CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory);

// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory))
        .build();