Ngăn xếp mạng

ExoPlayer thường được dùng để phát trực tuyến nội dung nghe nhìn qua Internet. Nó hỗ trợ nhiều bộ phần cứng và phần mềm mạng để thực hiện các yêu cầu mạng cơ bản. Lựa chọn của bạn về ngăn xếp mạng có thể ảnh hưởng đáng kể đến hiệu suất phát trực tuyến.

Trang này trình bày cách định cấu hình ExoPlayer để sử dụng ngăn xếp mạng mà bạn chọn, liệt kê các lựa chọn hiện có, đưa ra một số hướng dẫn về cách chọn ngăn xếp mạng cho ứng dụng của bạn và giải thích cách bật tính năng lưu vào bộ nhớ đệm cho nội dung nghe nhìn được phát trực tuyến.

Định cấu hình ExoPlayer để sử dụng một ngăn xếp mạng cụ thể

ExoPlayer tải dữ liệu thông qua các thành phần DataSource mà nó nhận được từ các thực thể DataSource.Factory được chèn từ mã ứng dụng.

Nếu ứng dụng của bạn chỉ cần phát nội dung http(s), thì việc chọn một ngăn xếp mạng cũng đơn giản như việc cập nhật mọi thực thể DataSource.Factory mà ứng dụng của bạn chèn vào thành các thực thể của HttpDataSource.Factory tương ứng với ngăn xếp mạng mà bạn muốn sử dụng. Nếu ứng dụng của bạn cũng cần phát nội dung không phải http(s), chẳng hạn như tệp cục bộ, hãy sử dụng DefaultDataSource.Factory:

Kotlin

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

Java

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

Trong ví dụ này, PreferredHttpDataSource.Factory là nhà máy tương ứng với ngăn xếp mạng ưu tiên của bạn. Lớp DefaultDataSource.Factory bổ sung hỗ trợ cho các nguồn không phải http(s), chẳng hạn như tệp cục bộ.

Ví dụ sau đây cho thấy cách tạo một ExoPlayer sẽ sử dụng ngăn xếp mạng Cronet và cũng hỗ trợ phát nội dung không phải 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();

Bộ phần cứng và phần mềm mạng được hỗ trợ

ExoPlayer hỗ trợ trực tiếp HttpEngine, Cronet, OkHttp và ngăn xếp mạng mặc định tích hợp của Android. Bạn cũng có thể mở rộng ExoPlayer để hỗ trợ mọi ngăn xếp mạng khác hoạt động trên Android.

HttpEngine

HttpEngine là ngăn xếp mạng mặc định được đề xuất trên Android từ API 34 (hoặc tiện ích S 7). Trong hầu hết các trường hợp, nó sử dụng ngăn xếp mạng Cronet nội bộ, hỗ trợ HTTP, HTTP/2 và HTTP/3 qua giao thức QUIC.

ExoPlayer hỗ trợ HttpEngine bằng HttpEngineDataSource.Factory. Bạn có thể chèn nhà máy nguồn dữ liệu này như mô tả trong phần Định cấu hình ExoPlayer để sử dụng một ngăn xếp mạng cụ thể.

Cronet

Cronet là bộ phần cứng và phần mềm mạng Chromium được dùng cho các ứng dụng Android dưới dạng thư viện. Cronet tận dụng nhiều công nghệ giúp giảm độ trễ và tăng công suất các yêu cầu về mạng mà ứng dụng của bạn cần để hoạt động, bao gồm cả những yêu cầu do ExoPlayer thực hiện. Nó hỗ trợ các giao thức HTTP, HTTP/2 và HTTP/3 qua QUIC một cách tự nhiên. Cronet được một số ứng dụng phát trực tuyến lớn nhất thế giới sử dụng, bao gồm cả YouTube.

ExoPlayer hỗ trợ Cronet thông qua thư viện Cronet. Hãy xem README.md của thư viện để biết hướng dẫn chi tiết về cách sử dụng. Xin lưu ý rằng thư viện Cronet có thể sử dụng 3 cách triển khai Cronet cơ bản:

  1. Dịch vụ Google Play: Chúng tôi đề xuất sử dụng phương thức triển khai này trong hầu hết các trường hợp và quay lại ngăn xếp mạng tích hợp của Android (DefaultHttpDataSource) nếu Dịch vụ Google Play không có sẵn.
  2. Cronet nhúng: Có thể là lựa chọn phù hợp nếu một tỷ lệ lớn người dùng của bạn ở những thị trường mà Dịch vụ Google Play không được cung cấp rộng rãi, hoặc nếu bạn muốn kiểm soát chính xác phiên bản của việc triển khai Cronet đang được sử dụng. Nhược điểm chính của Cronet Embedded là nó sẽ thêm khoảng 8 MB vào ứng dụng của bạn.
  3. Cronet dự phòng: Chế độ triển khai dự phòng của Cronet triển khai API của Cronet dưới dạng một trình bao bọc xung quanh bộ phần cứng và phần mềm mạng tích hợp của Android. Bạn không nên dùng thông số này với ExoPlayer, vì việc sử dụng trực tiếp ngăn xếp mạng tích hợp của Android (bằng cách dùng DefaultHttpDataSource) sẽ hiệu quả hơn.

OkHttp

OkHttp là một ngăn xếp mạng hiện đại khác được nhiều ứng dụng Android phổ biến sử dụng rộng rãi. Nó hỗ trợ HTTP và HTTP/2, nhưng chưa hỗ trợ HTTP/3 qua QUIC.

ExoPlayer hỗ trợ OkHttp thông qua thư viện OkHttp. Hãy xem README.md của thư viện để biết hướng dẫn chi tiết về cách sử dụng. Khi sử dụng thư viện OkHttp, ngăn xếp mạng sẽ được nhúng trong ứng dụng. Điều này tương tự như Cronet Embedded, tuy nhiên OkHttp nhỏ hơn đáng kể, chỉ thêm dưới 1 MB vào ứng dụng của bạn.

Bộ phần cứng và phần mềm mạng tích hợp sẵn của Android

ExoPlayer hỗ trợ việc sử dụng ngăn xếp mạng tích hợp của Android với DefaultHttpDataSourceDefaultHttpDataSource.Factory, đây là một phần của thư viện ExoPlayer cốt lõi.

Việc triển khai ngăn xếp mạng chính xác phụ thuộc vào phần mềm chạy trên thiết bị cơ bản. Trên hầu hết các thiết bị, chỉ HTTP được hỗ trợ (tức là HTTP/2 và HTTP/3 qua QUIC không được hỗ trợ).

Các bộ phần cứng và phần mềm mạng khác

Các ứng dụng cũng có thể tích hợp các ngăn xếp mạng khác với ExoPlayer. Để làm việc này, hãy triển khai một HttpDataSource bao bọc ngăn xếp mạng, cùng với một HttpDataSource.Factory tương ứng. Các thư viện Cronet và OkHttp của ExoPlayer là những ví dụ điển hình về cách thực hiện việc này.

Khi tích hợp với một ngăn xếp mạng Java thuần tuý, bạn nên triển khai một DataSourceContractTest để kiểm tra xem việc triển khai HttpDataSource của bạn có hoạt động đúng cách hay không. OkHttpDataSourceContractTest trong thư viện OkHttp là một ví dụ hay về cách thực hiện việc này.

Chọn một bộ phần cứng và phần mềm mạng

Bảng sau đây trình bày ưu và nhược điểm của các ngăn xếp mạng mà ExoPlayer hỗ trợ.

Bộ phần cứng và phần mềm mạng Giao thức Tác động của kích thước APK Ghi chú
HttpEngine HTTP
HTTP/2
HTTP/3 qua QUIC
Không Chỉ có trên API 34 hoặc Tiện ích S 7
Cronet (Dịch vụ Google Play) HTTP
HTTP/2
HTTP/3 qua QUIC
Nhỏ
(<100KB)
Yêu cầu có Dịch vụ Google Play. Phiên bản Cronet được tự động cập nhật
Cronet (Được nhúng) HTTP
HTTP/2
HTTP/3 qua QUIC
Lớn
(~8 MB)
Nhà phát triển ứng dụng kiểm soát phiên bản Cronet
Cronet (Dự phòng) HTTP
(tuỳ theo thiết bị)
Nhỏ
(<100KB)
Không nên dùng cho ExoPlayer
OkHttp HTTP
HTTP/2
Nhỏ
(<1 MB)
Bộ phần cứng và phần mềm mạng tích hợp HTTP
(tuỳ theo thiết bị)
Không Cách triển khai sẽ khác nhau tuỳ theo thiết bị

Các giao thức HTTP/2 và HTTP/3 qua QUIC có thể cải thiện đáng kể hiệu suất truyền phát trực tiếp nội dung nghe nhìn. Cụ thể, khi phát trực tuyến nội dung đa phương tiện thích ứng được phân phối bằng mạng phân phối nội dung (CDN), có những trường hợp việc sử dụng các giao thức này có thể giúp CDN hoạt động hiệu quả hơn nhiều. Vì lý do này, việc HttpEngine và Cronet hỗ trợ cả HTTP/2 và HTTP/3 qua QUIC (cũng như việc OkHttp hỗ trợ HTTP/2) là một lợi ích lớn so với việc sử dụng ngăn xếp mạng tích hợp của Android, miễn là các máy chủ lưu trữ nội dung cũng hỗ trợ các giao thức này.

Khi cân nhắc việc phát trực tuyến nội dung nghe nhìn riêng biệt, bạn nên sử dụng HttpEngine hoặc Cronet do Dịch vụ Google Play cung cấp, quay trở lại DefaultHttpDataSource nếu Dịch vụ Google Play không hoạt động. Đề xuất này cân bằng giữa việc cho phép sử dụng HTTP/2 và HTTP/3 qua QUIC trên hầu hết các thiết bị, đồng thời tránh tăng đáng kể kích thước APK. Đề xuất này có một số trường hợp ngoại lệ. Đối với những trường hợp Dịch vụ Google Play có thể không hoạt động trên một phần đáng kể các thiết bị sẽ chạy ứng dụng của bạn, thì việc sử dụng Cronet Embedded hoặc OkHttp có thể phù hợp hơn. Bạn có thể chấp nhận việc sử dụng ngăn xếp mạng tích hợp nếu kích thước APK là một vấn đề quan trọng hoặc nếu truyền phát trực tiếp nội dung nghe nhìn chỉ là một phần nhỏ trong chức năng của ứng dụng.

Ngoài phương tiện, bạn nên chọn một ngăn xếp mạng duy nhất cho tất cả hoạt động kết nối mạng do ứng dụng của bạn thực hiện. Điều này cho phép các tài nguyên (chẳng hạn như ổ cắm) được nhóm và chia sẻ hiệu quả giữa ExoPlayer và các thành phần ứng dụng khác.

Vì ứng dụng của bạn có thể sẽ cần thực hiện hoạt động kết nối mạng không liên quan đến việc phát nội dung nghe nhìn, nên lựa chọn của bạn về ngăn xếp mạng cuối cùng phải tính đến các đề xuất nêu trên của chúng tôi về việc truyền phát nội dung nghe nhìn riêng biệt, các yêu cầu của mọi thành phần khác thực hiện hoạt động kết nối mạng và tầm quan trọng tương đối của các thành phần đó đối với ứng dụng của bạn.

Lưu nội dung nghe nhìn vào bộ nhớ đệm

ExoPlayer hỗ trợ lưu các byte đã tải vào bộ nhớ đệm trên đĩa để tránh tải cùng một byte nhiều lần từ mạng. Thao tác này hữu ích khi bạn muốn tua lại trong nội dung nghe nhìn hiện tại hoặc lặp lại cùng một mục.

Hoạt động lưu vào bộ nhớ đệm yêu cầu một phiên bản SimpleCache trỏ đến một thư mục bộ nhớ đệm chuyên dụng và một 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();