Netzwerkstacks

ExoPlayer wird häufig zum Streamen von Medien über das Internet verwendet. Es unterstützt mehrere Netzwerkstacks für die zugrunde liegenden Netzwerkanfragen. Die Wahl des Netzwerk-Stacks kann sich erheblich auf die Streaming-Leistung auswirken.

Auf dieser Seite wird beschrieben, wie Sie ExoPlayer so konfigurieren, dass der von Ihnen ausgewählte Netzwerk-Stack verwendet wird. Außerdem werden die verfügbaren Optionen aufgeführt, einige Hinweise zur Auswahl eines Netzwerk-Stacks für Ihre App gegeben und erläutert, wie Sie das Caching für gestreamte Medien aktivieren.

ExoPlayer für die Verwendung eines bestimmten Netzwerkstacks konfigurieren

ExoPlayer lädt Daten über DataSource-Komponenten, die von DataSource.Factory-Instanzen abgerufen werden, die aus dem App-Code eingefügt werden.

Wenn Ihre App nur http(s)-Inhalte abspielen muss, können Sie einen Netzwerk-Stack auswählen, indem Sie alle DataSource.Factory-Instanzen, die Ihre App einfügt, in Instanzen von HttpDataSource.Factory ändern, die dem gewünschten Netzwerk-Stack entsprechen. Wenn Ihre App auch nicht-http(s)-Inhalte wie lokale Dateien abspielen muss, verwenden Sie DefaultDataSource.Factory:

Kotlin

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

Java

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

In diesem Beispiel ist PreferredHttpDataSource.Factory die Fabrik, die Ihrem bevorzugten Netzwerk-Stack entspricht. Die DefaultDataSource.Factory-Ebene bietet Unterstützung für Nicht-http(s)-Quellen wie lokale Dateien.

Das folgende Beispiel zeigt, wie Sie ein ExoPlayer erstellen, das den Cronet-Netzwerk-Stack verwendet und auch die Wiedergabe von Nicht-http(s)-Inhalten unterstützt.

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

Unterstützte Netzwerkstacks

ExoPlayer bietet direkte Unterstützung für HttpEngine, Cronet, OkHttp und den integrierten Standardnetzwerk-Stack von Android. ExoPlayer kann auch erweitert werden, um jeden anderen Netzwerk-Stack zu unterstützen, der unter Android funktioniert.

HttpEngine

HttpEngine ist der empfohlene Standard-Netzwerk-Stack auf Android ab API 34 (oder S-Erweiterungen 7). In den meisten Fällen wird intern der Cronet-Netzwerk-Stack verwendet, der die Protokolle HTTP, HTTP/2 und HTTP/3 über QUIC unterstützt.

ExoPlayer unterstützt HttpEngine mit dem HttpEngineDataSource.Factory. Sie können diese Datenquellen-Factory wie unter ExoPlayer für die Verwendung eines bestimmten Netzwerk-Stacks konfigurieren beschrieben einfügen.

Cronet

Cronet ist der Chromium-Netzwerk-Stack, der Android-Apps als Bibliothek zur Verfügung gestellt wird. Cronet nutzt mehrere Technologien, die die Latenz verringern und den Durchsatz der Netzwerkanfragen erhöhen, die für die Funktion Ihrer App erforderlich sind, einschließlich der von ExoPlayer gestellten Anfragen. Es unterstützt nativ die Protokolle HTTP, HTTP/2 und HTTP/3 über QUIC. Cronet wird von einigen der weltweit größten Streaming-Apps verwendet, darunter YouTube.

ExoPlayer unterstützt Cronet über die Cronet-Bibliothek. Eine ausführliche Anleitung zur Verwendung der Bibliothek finden Sie in der README.md. Die Cronet-Bibliothek kann drei zugrunde liegende Cronet-Implementierungen verwenden:

  1. Google Play-Dienste:Wir empfehlen, diese Implementierung in den meisten Fällen zu verwenden und auf den integrierten Netzwerk-Stack von Android (DefaultHttpDataSource) zurückzugreifen, wenn die Google Play-Dienste nicht verfügbar sind.
  2. Cronet Embedded:Diese Option ist möglicherweise eine gute Wahl, wenn ein großer Prozentsatz Ihrer Nutzer in Märkten ansässig ist, in denen Google Play-Dienste nicht weit verbreitet sind, oder wenn Sie die genaue Version der verwendeten Cronet-Implementierung steuern möchten. Der größte Nachteil von Cronet Embedded ist, dass es Ihre App um etwa 8 MB vergrößert.
  3. Cronet-Fallback:Die Fallback-Implementierung von Cronet implementiert die API von Cronet als Wrapper um den integrierten Netzwerk-Stack von Android. Sie sollte nicht mit ExoPlayer verwendet werden, da die direkte Verwendung des integrierten Netzwerk-Stacks von Android (mit DefaultHttpDataSource) effizienter ist.

OkHttp

OkHttp ist ein weiterer moderner Netzwerk-Stack, der von vielen beliebten Android-Apps verwendet wird. Es unterstützt HTTP und HTTP/2, aber noch nicht HTTP/3 über QUIC.

ExoPlayer unterstützt OkHttp über die OkHttp-Bibliothek. Eine ausführliche Anleitung zur Verwendung der Bibliothek finden Sie in der README.md. Wenn Sie die OkHttp-Bibliothek verwenden, ist der Netzwerk-Stack in die App eingebettet. Das ist ähnlich wie bei Cronet Embedded, OkHttp ist jedoch deutlich kleiner und fügt Ihrer App weniger als 1 MB hinzu.

In Android integrierter Netzwerk-Stack

ExoPlayer unterstützt die Verwendung des integrierten Netzwerkstacks von Android mit DefaultHttpDataSource und DefaultHttpDataSource.Factory, die Teil der ExoPlayer-Kernbibliothek sind.

Die genaue Implementierung des Netzwerk-Stacks hängt von der Software ab, die auf dem zugrunde liegenden Gerät ausgeführt wird. Auf den meisten Geräten wird nur HTTP unterstützt. HTTP/2 und HTTP/3 über QUIC werden nicht unterstützt.

Andere Protokollstapel

Apps können auch andere Netzwerk-Stacks in ExoPlayer einbinden. Implementieren Sie dazu eine HttpDataSource, die den Netzwerk-Stack umschließt, sowie eine entsprechende HttpDataSource.Factory. Die Cronet- und OkHttp-Bibliotheken von ExoPlayer sind gute Beispiele dafür.

Bei der Integration in einen reinen Java-Netzwerk-Stack empfiehlt es sich, eine DataSourceContractTest zu implementieren, um zu prüfen, ob sich Ihre HttpDataSource-Implementierung korrekt verhält. OkHttpDataSourceContractTest in der OkHttp-Bibliothek ist ein gutes Beispiel dafür.

Netzwerkstack auswählen

In der folgenden Tabelle sind die Vor- und Nachteile der von ExoPlayer unterstützten Netzwerkstacks aufgeführt.

Protokollstapel Protokolle Auswirkungen der APK-Größe Hinweise
HttpEngine HTTP
HTTP/2
HTTP/3 über QUIC
Keine Nur auf API 34 oder S Extensions 7 verfügbar
Cronet (Google Play-Dienste) HTTP
HTTP/2
HTTP/3 über QUIC
Klein
(<100 KB)
Erfordert die Google Play-Dienste. Cronet-Version automatisch aktualisiert
Cronet (eingebettet) HTTP
HTTP/2
HTTP/3 über QUIC
Groß
(~8 MB)
Cronet-Version wird vom App-Entwickler gesteuert
Cronet (Fallback) HTTP
(variiert je nach Gerät)
Klein
(<100 KB)
Nicht für ExoPlayer empfohlen
OkHttp HTTP
HTTP/2
Klein
(<1 MB)
Integrierter Netzwerk-Stack HTTP
(variiert je nach Gerät)
Keine Die Implementierung variiert je nach Gerät.

Die Protokolle HTTP/2 und HTTP/3 über QUIC können die Leistung beim Media-Streaming erheblich verbessern. Insbesondere beim Streamen adaptiver Medien, die über ein Content Delivery Network (CDN) verteilt werden, gibt es Fälle, in denen die Verwendung dieser Protokolle CDNs einen viel effizienteren Betrieb ermöglicht. Daher ist die Unterstützung von HTTP/2 und HTTP/3 über QUIC durch HttpEngine und Cronet (und die Unterstützung von HTTP/2 durch OkHttp) ein großer Vorteil im Vergleich zur Verwendung des integrierten Netzwerkstacks von Android, sofern die Server, auf denen die Inhalte gehostet werden, diese Protokolle ebenfalls unterstützen.

Wenn Sie nur Media-Streaming in Betracht ziehen, empfehlen wir die Verwendung von HttpEngine oder Cronet, die von Google Play-Diensten bereitgestellt werden, und als Fallback DefaultHttpDataSource, wenn Google Play-Dienste nicht verfügbar sind. Diese Empfehlung bietet ein gutes Gleichgewicht zwischen der Aktivierung von HTTP/2 und HTTP/3 über QUIC auf den meisten Geräten und der Vermeidung einer erheblichen Steigerung der APK-Größe. Es gibt Ausnahmen von dieser Empfehlung. In Fällen, in denen die Google Play-Dienste wahrscheinlich auf einem erheblichen Teil der Geräte, auf denen Ihre App ausgeführt wird, nicht verfügbar sind, ist die Verwendung von Cronet Embedded oder OkHttp möglicherweise besser geeignet. Die Verwendung des integrierten Netzwerk-Stacks kann akzeptabel sein, wenn die APK-Größe ein wichtiger Faktor ist oder wenn das Media-Streaming nur ein kleiner Teil der Funktionen Ihrer App ist.

Neben Medien ist es normalerweise eine gute Idee, einen einzelnen Netzwerk-Stack für alle Netzwerkaktivitäten Ihrer App zu verwenden. So können Ressourcen (z. B. Sockets) effizient gebündelt und zwischen ExoPlayer und anderen App-Komponenten geteilt werden.

Da Ihre App höchstwahrscheinlich Netzwerkaktivitäten ausführen muss, die nicht mit der Medienwiedergabe zusammenhängen, sollten Sie bei der Auswahl des Netzwerk-Stacks letztendlich unsere Empfehlungen oben für das Media-Streaming, die Anforderungen aller anderen Komponenten, die Netzwerkaktivitäten ausführen, und deren relative Bedeutung für Ihre App berücksichtigen.

Medien im Cache speichern

ExoPlayer unterstützt das Caching geladener Byte auf der Festplatte, um zu verhindern, dass dieselben Byte wiederholt aus dem Netzwerk geladen werden. Das ist nützlich, wenn Sie im aktuellen Medium zurückspulen oder dasselbe Element wiederholen möchten.

Für das Caching ist eine SimpleCache-Instanz erforderlich, die auf ein dediziertes Cache-Verzeichnis verweist, sowie eine 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();