Netzwerkstacks

ExoPlayer wird häufig für das Streamen von Medien über das Internet verwendet. Es unterstützt mehrere Netzwerkstacks für die zugrunde liegenden Netzwerkanfragen. Die Wahl des Netzwerkstacks kann erhebliche Auswirkungen auf die Streaming-Leistung haben.

Auf dieser Seite wird beschrieben, wie Sie ExoPlayer so konfigurieren, dass der gewünschte Netzwerkstack verwendet wird. Außerdem werden die verfügbaren Optionen aufgeführt, es wird erläutert, wie Sie einen Netzwerkstack für Ihre App auswählen, und es wird erklärt, 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 aus DataSource.Factory-Instanzen abgerufen werden, die aus dem App-Code eingefügt werden.

Wenn Ihre App nur http(s)-Inhalte wiedergeben muss, ist die Auswahl eines Netzwerkstacks so einfach wie das Aktualisieren aller DataSource.Factory-Instanzen, die Ihre App einfügt, auf Instanzen des HttpDataSource.Factory, der dem gewünschten Netzwerkstack entspricht. Wenn Ihre App auch Nicht-HTTP(S)-Inhalte wie lokale Dateien abspielen muss, verwenden Sie DefaultDataSource.Factory:

Kotlin

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

Java

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

In diesem Beispiel ist PreferredHttpDataSource.Factory die Factory, die Ihrem bevorzugten Netzwerkstack entspricht. Die DefaultDataSource.Factory-Ebene bietet Unterstützung für Nicht-HTTP(S)-Quellen wie lokale Dateien.

Im folgenden Beispiel wird gezeigt, wie Sie einen ExoPlayer erstellen, der den Cronet-Netzwerkstack 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 Standard-Netzwerkstack von Android. ExoPlayer kann auch erweitert werden, um jeden anderen Netzwerkstack zu unterstützen, der unter Android funktioniert.

HttpEngine

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

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

Cronet

Cronet ist der Chromium-Netzwerkstack, der Android-Apps als Bibliothek zur Verfügung gestellt wird. Cronet nutzt mehrere Technologien, die die Latenz reduzieren und den Durchsatz der Netzwerkanfragen erhöhen, die Ihre App für die Ausführung benötigt, 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 Netzwerkstack von Android (DefaultHttpDataSource) zurückzugreifen, wenn die Google Play-Dienste nicht verfügbar sind.
  2. Cronet Embedded:Kann eine gute Wahl sein, wenn ein großer Teil Ihrer Nutzer in Märkten ansässig ist, in denen die 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 Ihre App dadurch um etwa 8 MB größer wird.
  3. Cronet-Fallback: Die Fallback-Implementierung von Cronet implementiert die Cronet API als Wrapper für den integrierten Netzwerkstack von Android. Sie sollte nicht mit ExoPlayer verwendet werden, da die direkte Verwendung des integrierten Netzwerkstacks von Android (mit DefaultHttpDataSource) effizienter ist.

OkHttp

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

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 Netzwerkstack in die App eingebettet. Das ähnelt Cronet Embedded, aber OkHttp ist deutlich kleiner und fügt Ihrer App weniger als 1 MB hinzu.

In Android integrierter Netzwerkstack

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 Netzwerkstacks 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 (d. h. HTTP/2 und HTTP/3 über QUIC werden nicht unterstützt).

Andere Netzwerkstacks

Apps können auch andere Netzwerkstacks in ExoPlayer einbinden. Implementieren Sie dazu einen HttpDataSource, der den Netzwerkstack umschließt, zusammen mit einem entsprechenden HttpDataSource.Factory. Die Cronet- und OkHttp-Bibliotheken von ExoPlayer sind gute Beispiele dafür, wie das geht.

Wenn du die Integration mit einem reinen Java-Netzwerkstack vornimmst, ist es sinnvoll, einen DataSourceContractTest zu implementieren, um zu prüfen, ob sich deine HttpDataSource-Implementierung korrekt verhält. OkHttpDataSourceContractTest in der OkHttp-Bibliothek ist ein gutes Beispiel dafür, wie das geht.

Netzwerkstack auswählen

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

Netzwerkstack Protokolle Auswirkungen auf die APK-Größe Hinweise
HttpEngine HTTP
HTTP/2
HTTP/3 über QUIC
Keine Nur verfügbar für API 34 oder S Extensions 7
Cronet (Google Play-Dienste) HTTP
HTTP/2
HTTP/3 über QUIC
Klein
(< 100 KB)
Google Play-Dienste erforderlich. Cronet-Version wird automatisch aktualisiert
Cronet (eingebettet) HTTP
HTTP/2
HTTP/3 über QUIC
Groß
(~8 MB)
Cronet-Version wird vom App-Entwickler gesteuert
Cronet (Fallback) HTTP
(je nach Gerät unterschiedlich)
Klein
(< 100 KB)
Nicht empfohlen für ExoPlayer
OkHttp HTTP
HTTP/2
Klein
(< 1 MB)
Integrierter Netzwerkstack 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 Medienstreaming erheblich verbessern. Insbesondere beim Streaming adaptiver Medien, die über ein Content Distribution Network (CDN) verteilt werden, können CDNs durch die Verwendung dieser Protokolle in einigen Fällen viel effizienter arbeiten. Aus diesem Grund 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 gegenüber der Verwendung des integrierten Netzwerkstacks von Android, sofern die Server, auf denen die Inhalte gehostet werden, diese Protokolle ebenfalls unterstützen.

Wenn Sie nur Medienstreaming in Betracht ziehen, empfehlen wir die Verwendung von HttpEngine oder Cronet, die von den Google Play-Diensten bereitgestellt werden, mit einem Fallback auf DefaultHttpDataSource, wenn die Google Play-Dienste nicht verfügbar sind. Diese Empfehlung bietet ein gutes Gleichgewicht zwischen der Ermöglichung der Verwendung von HTTP/2 und HTTP/3 über QUIC auf den meisten Geräten und der Vermeidung einer erheblichen Erhöhung der APK-Größe. Es gibt Ausnahmen von dieser Empfehlung. Wenn die Google Play-Dienste auf einem erheblichen Teil der Geräte, auf denen Ihre App ausgeführt wird, wahrscheinlich nicht verfügbar sind, ist die Verwendung von Cronet Embedded oder OkHttp möglicherweise besser geeignet. Die Verwendung des integrierten Netzwerkstacks kann akzeptabel sein, wenn die APK-Größe ein wichtiges Kriterium ist oder wenn das Medienstreaming nur einen kleinen Teil der Funktionen Ihrer App ausmacht.

Es ist normalerweise eine gute Idee, nicht nur für Medien, sondern für alle Netzwerkaktivitäten Ihrer App einen einzigen Netzwerkstack auszuwählen. Dadurch können Ressourcen (z. B. Sockets) effizient zusammengefasst und zwischen ExoPlayer und anderen App-Komponenten geteilt werden.

Da deine App wahrscheinlich auch Netzwerkaufgaben ausführen muss, die nicht mit der Medienwiedergabe zusammenhängen, solltest du bei der Wahl des Netzwerkstacks unsere oben genannten Empfehlungen für das Medienstreaming, die Anforderungen anderer Komponenten, die Netzwerkaufgaben ausführen, und ihre relative Bedeutung für deine App berücksichtigen.

Medien im Cache speichern

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

Für das Caching ist eine SimpleCache-Instanz erforderlich, die auf ein dediziertes Cache-Verzeichnis und eine CacheDataSource.Factory verweist:

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