Stack di rete

ExoPlayer viene comunemente utilizzato per lo streaming di contenuti multimediali su Internet. Supporta più stack di rete per effettuare le richieste di rete sottostanti. La scelta dello stack di rete può avere un impatto significativo sulle prestazioni dei flussi di dati.

Questa pagina descrive come configurare ExoPlayer per l'utilizzo del tuo stack di rete preferito, elenca le opzioni disponibili, fornisce alcune indicazioni su come scegliere uno stack di rete per la tua app e spiega come abilitare la memorizzazione nella cache per i contenuti multimediali in streaming.

Configurazione di ExoPlayer per l'uso di uno stack di rete specifico

ExoPlayer carica i dati tramite i componenti DataSource, che vengono ottenuti dalle istanze DataSource.Factory inserite dal codice dell'app.

Se la tua app deve riprodurre soltanto contenuti http(s), la selezione di uno stack di rete è semplice come aggiornare tutte le istanze DataSource.Factory inserite dalla tua app come istanze di HttpDataSource.Factory corrispondenti allo stack di rete che vuoi utilizzare. Se l'app deve riprodurre anche contenuti non HTTP, ad esempio file locali, utilizza DefaultDataSource.Factory:

Kotlin

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

Java

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

In questo esempio, PreferredHttpDataSource.Factory è il valore di fabbrica corrispondente al tuo stack di rete preferito. Il livello DefaultDataSource.Factory aggiunge il supporto per origini non HTTP, come i file locali.

L'esempio seguente mostra come creare un elemento ExoPlayer che utilizzerà lo stack di rete Cronet e supporterà anche la riproduzione di contenuti non HTTP.

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

Stack di rete supportati

ExoPlayer supporta direttamente lo stack di rete integrato di Cronet, OkHttp e Android. ExoPlayer può anche essere esteso per supportare qualsiasi altro stack di rete funzionante su Android.

Cronet

Cronet è lo stack di rete Chromium reso disponibile per le app per Android sotto forma di libreria. Cronet sfrutta diverse tecnologie che riducono la latenza e aumentano la velocità effettiva delle richieste di rete necessarie per il funzionamento dell'app, incluse quelle effettuate da ExoPlayer. Supporta in modo nativo i protocolli HTTP, HTTP/2 e HTTP/3 tramite protocolli QUIC. Cronet è utilizzato da alcune delle app di streaming più importanti al mondo, tra cui YouTube.

ExoPlayer supporta Cronet tramite la sua libreria Cronet. Consulta le README.md della libreria per istruzioni dettagliate su come utilizzarla. Nota che la libreria Cronet è in grado di utilizzare tre implementazioni di Cronet sottostanti:

  1. Google Play Services: consigliamo di utilizzare questa implementazione nella maggior parte dei casi e di ricorrere allo stack di rete integrato di Android (DefaultHttpDataSource) se Google Play Services non è disponibile.
  2. Cronet Embedded: può essere una buona scelta se un'ampia percentuale di utenti si trova in mercati in cui Google Play Services non è ampiamente disponibile o se vuoi controllare la versione esatta dell'implementazione di Cronet utilizzata. Lo svantaggio principale di Cronet Embedded è che aggiunge circa 8 MB alla tua app.
  3. Cronet di riserva: l'implementazione di riserva di Cronet implementa l'API di Cronet come wrapper intorno allo stack di rete integrato di Android. Non deve essere utilizzato con ExoPlayer, dato che l'utilizzo diretto dello stack di rete integrato di Android (utilizzando DefaultHttpDataSource) è più efficiente.

OkHttp

OkHttp è un altro stack di rete moderno ampiamente usato da molte app Android popolari. Supporta HTTP e HTTP/2, ma non supporta ancora HTTP/3 tramite QUIC.

ExoPlayer supporta OkHttp tramite la sua libreria OkHttp. Consulta le README.md della libreria per istruzioni dettagliate su come utilizzarla. Quando utilizzi la libreria OkHttp, lo stack di rete è incorporato all'interno dell'app. È simile a Cronet Embedded, tuttavia OkHttp è molto più piccolo, aggiungendo meno di 1 MB alla tua app.

Stack di rete integrato di Android

ExoPlayer supporta l'uso dello stack di rete integrato di Android con DefaultHttpDataSource e DefaultHttpDataSource.Factory, che fanno parte della libreria di base di ExoPlayer.

L'esatta implementazione dello stack di rete dipende dal software in esecuzione sul dispositivo sottostante. Sulla maggior parte dei dispositivi (a partire dal 2021) è supportato solo HTTP (ovvero HTTP/2 e HTTP/3 tramite QUIC non sono supportati).

Altri stack di rete

Le app possono anche integrare altri stack di rete con ExoPlayer. A questo scopo, implementa un'HttpDataSource che aggrega lo stack di rete, insieme a un HttpDataSource.Factory corrispondente. Le librerie Cronet e OkHttp di ExoPlayer sono buoni esempi di come farlo.

Durante l'integrazione con uno stack di rete Java puro, è consigliabile implementare un elemento DataSourceContractTest per verificare che l'implementazione di HttpDataSource funzioni correttamente. OkHttpDataSourceContractTest nella libreria OkHttp è un buon esempio di come fare.

Scelta di uno stack di rete

La seguente tabella illustra i pro e i contro degli stack di rete supportati da ExoPlayer.

Stack di rete Protocolli Impatto sulle dimensioni degli APK Notes
Cronet (Google Play Services) HTTP
HTTP/2
HTTP/3 su QUIC
Piccola
(<100 kB)
Richiede Google Play Services. Versione Cronet aggiornata automaticamente
Cronet (incorporato) HTTP
HTTP/2
HTTP/3 su QUIC
Grande
(~8 MB)
Versione di Cronet controllata dallo sviluppatore di app
Cronet (di riserva) HTTP
(varia a seconda del dispositivo)
Piccola
(<100 kB)
Sconsigliato per ExoPlayer
OkHttp HTTP
HTTP/2
Piccola
(<1 MB)
Richiede runtime Kotlin
Stack di rete integrato HTTP
(varia a seconda del dispositivo)
Nessun valore L'implementazione varia in base al dispositivo

I protocolli HTTP/2 e HTTP/3 su QUIC possono migliorare notevolmente le prestazioni dei flussi di dati multimediali. In particolare, durante lo streaming di contenuti multimediali adattivi distribuiti tramite una rete di distribuzione di contenuti (CDN), in alcuni casi l'uso di questi protocolli può consentire alle reti CDN di funzionare in modo molto più efficiente. Per questo motivo, il supporto di Chromet per HTTP/2 e HTTP/3 tramite QUIC (e il supporto di OkHttp per HTTP/2) è un vantaggio importante rispetto all'utilizzo dello stack di rete integrato di Android, a condizione che i server su cui sono ospitati i contenuti supportino anche questi protocolli.

Per prendere in considerazione lo streaming multimediale separatamente, ti consigliamo di utilizzare il Cronet fornito da Google Play Services. Se Google Play Services non è disponibile, torna a DefaultHttpDataSource. Questo consiglio rappresenta un buon equilibrio tra consentire l'utilizzo di HTTP/2 e HTTP/3 rispetto a QUIC sulla maggior parte dei dispositivi e evitare un aumento significativo delle dimensioni degli APK. Ci sono delle eccezioni a questo consiglio. Per i casi in cui è probabile che Google Play Services non sia disponibile su una parte significativa di dispositivi su cui verrà eseguita la tua app, l'utilizzo di Cronet Embedded o OkHttp potrebbe essere più appropriato. L'utilizzo dello stack di rete integrato potrebbe essere accettabile se le dimensioni dell'APK rappresentano un problema critico o se lo streaming di contenuti multimediali rappresenta solo una parte minima della funzionalità della tua app.

Oltre ai contenuti multimediali, di solito è opportuno scegliere un unico stack di rete per tutte le reti eseguite dalla tua app. In questo modo le risorse (ad esempio i socket) possono essere condivise in modo efficiente in pool e condivise tra ExoPlayer e altri componenti dell'app.

Poiché è molto probabile che la tua app debba eseguire operazioni di networking non correlate alla riproduzione di contenuti multimediali, la scelta dello stack di rete dovrebbe basarsi sui nostri consigli precedenti per lo streaming di contenuti multimediali in modo isolato, sui requisiti di eventuali altri componenti che eseguono il networking e sulla loro importanza relativa per la tua app.

Memorizzazione nella cache dei contenuti multimediali

ExoPlayer supporta la memorizzazione nella cache dei byte caricati su disco per impedire il caricamento ripetuto degli stessi byte dalla rete. È utile per cercare il contenuto multimediale corrente o ripetere lo stesso elemento.

La memorizzazione nella cache richiede un'istanza SimpleCache che punti a una directory della cache dedicata e a una 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();