Tạo một ứng dụng trình phát nội dung đa phương tiện cơ bản bằng Media3 ExoPlayer

Jetpack Media3 xác định giao diện Player trình bày chức năng cơ bản để phát các tệp video và âm thanh. ExoPlayer là phương thức triển khai mặc định của giao diện này trong Media3. Bạn nên sử dụng ExoPlayer, vì thư viện này cung cấp bộ tính năng toàn diện phù hợp với hầu hết các trường hợp sử dụng phát và có thể tuỳ chỉnh để xử lý mọi trường hợp sử dụng bổ sung mà bạn có thể gặp phải. ExoPlayer cũng loại bỏ sự phân mảnh hệ điều hành và thiết bị để mã của bạn hoạt động nhất quán trên toàn bộ hệ sinh thái Android. ExoPlayer bao gồm:

Trang này sẽ hướng dẫn bạn một số bước chính để xây dựng ứng dụng phát. Để biết thêm thông tin, bạn có thể xem hướng dẫn đầy đủ của chúng tôi về Media3 ExoPlayer.

Bắt đầu

Để bắt đầu, hãy thêm phần phụ thuộc vào các mô-đun ExoPlayer, giao diện người dùng và phổ biến của Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.3.1"
implementation "androidx.media3:media3-ui:1.3.1"
implementation "androidx.media3:media3-common:1.3.1"

Tuỳ thuộc vào trường hợp sử dụng, có thể bạn cũng cần thêm các mô-đun từ Media3, chẳng hạn như exoplayer-dash để phát luồng ở định dạng DASH.

Hãy nhớ thay thế 1.3.1 bằng phiên bản thư viện mà bạn muốn. Bạn có thể tham khảo ghi chú phát hành để xem phiên bản mới nhất.

Tạo trình phát nội dung đa phương tiện

Với Media3, bạn có thể sử dụng phương thức triển khai có sẵn của giao diện Player, ExoPlayer hoặc tạo phương thức triển khai tuỳ chỉnh của riêng mình.

Tạo ExoPlayer

Cách đơn giản nhất để tạo một thực thể ExoPlayer như sau:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

Bạn có thể tạo trình phát nội dung đa phương tiện trong phương thức vòng đời onCreate() của Activity, Fragment hoặc Service nơi chứa nội dung đó.

Builder bao gồm nhiều lựa chọn tuỳ chỉnh mà bạn có thể quan tâm, chẳng hạn như:

Media3 cung cấp thành phần giao diện người dùng PlayerView mà bạn có thể đưa vào tệp bố cục của ứng dụng. Thành phần này đóng gói một PlayerControlView để điều khiển chế độ phát, SubtitleView để hiển thị phụ đề và Surface để kết xuất video.

Chuẩn bị trình phát

Thêm các mục nội dung nghe nhìn vào danh sách phát để phát bằng các phương thức như setMediaItem()addMediaItem(). Sau đó, hãy gọi prepare() để bắt đầu tải nội dung nghe nhìn và thu thập các tài nguyên cần thiết.

Bạn không nên thực hiện các bước này trước khi ứng dụng chạy trên nền trước. Nếu người chơi đang ở Activity hoặc Fragment, tức là bạn chuẩn bị người chơi trong phương thức vòng đời onStart() trên API cấp 24 trở lên hoặc phương thức vòng đời onResume() trên API cấp 23 trở xuống. Đối với trình phát nằm trong Service, bạn có thể chuẩn bị mã này trong onCreate().

Điều khiển trình phát

Sau khi chuẩn bị trình phát, bạn có thể điều khiển quá trình phát bằng cách gọi các phương thức trên trình phát, chẳng hạn như:

Các thành phần giao diện người dùng như PlayerView hoặc PlayerControlView sẽ cập nhật tương ứng khi được liên kết với người chơi.

Thả trình phát

Tính năng phát có thể cần đến các tài nguyên hạn chế, chẳng hạn như bộ giải mã video. Vì vậy, bạn cần gọi release() trên trình phát để giải phóng tài nguyên khi trình phát không còn cần đến.

Nếu người chơi đang ở Activity hoặc Fragment, hãy huỷ bỏ người chơi trong phương thức vòng đời onStop() trên API cấp 24 trở lên hoặc phương thức onPause() trên API cấp 23 trở xuống. Đối với người chơi ở trong Service, bạn có thể phát hành trong onDestroy().

Quản lý chế độ phát bằng một phiên phát nội dung nghe nhìn

Trên Android, phiên phát nội dung đa phương tiện là một cách thức chuẩn hoá để tương tác với trình phát nội dung nghe nhìn xuyên qua ranh giới của quy trình. Việc kết nối một phiên phát nội dung nghe nhìn với trình phát cho phép bạn quảng cáo tính năng phát nội dung nghe nhìn ra bên ngoài và nhận lệnh phát từ các nguồn bên ngoài, chẳng hạn như tích hợp với các chế độ điều khiển nội dung nghe nhìn của hệ thống trên thiết bị di động và thiết bị màn hình lớn.

Để sử dụng phiên phát nội dung nghe nhìn, hãy thêm phần phụ thuộc vào mô-đun Phiên Media3:

implementation "androidx.media3:media3-session:1.3.1"

Tạo một phiên nội dung nghe nhìn

Bạn có thể tạo MediaSession sau khi khởi tạo trình phát như sau:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 tự động đồng bộ hoá trạng thái của Player với trạng thái của MediaSession. Tính năng này hoạt động với mọi cách triển khai Player, bao gồm cả ExoPlayer, CastPlayer hoặc cách triển khai tuỳ chỉnh.

Cấp quyền kiểm soát cho các ứng dụng khách khác

Các ứng dụng khách có thể triển khai trình điều khiển nội dung nghe nhìn để điều khiển chế độ phát phiên phát nội dung nghe nhìn của bạn. Để nhận những yêu cầu này, hãy đặt một đối tượng callback khi tạo MediaSession.

Khi tay điều khiển sắp kết nối với phiên phát nội dung đa phương tiện, phương thức onConnect() sẽ được gọi. Bạn có thể sử dụng ControllerInfo được cung cấp để quyết định xem nên chấp nhận hay từ chối yêu cầu. Hãy xem ví dụ về việc này trong ứng dụng minh hoạ Phiên Media3.

Sau khi kết nối, bộ điều khiển có thể gửi lệnh phát đến phiên này. Sau đó, phiên hoạt động sẽ uỷ quyền các lệnh đó cho người chơi. Các lệnh phát và danh sách phát đã xác định trong giao diện Player sẽ được phiên xử lý tự động.

Các phương thức gọi lại khác cho phép bạn xử lý, chẳng hạn như yêu cầu các lệnh phát tuỳ chỉnhsửa đổi danh sách phát. Các lệnh gọi lại này tương tự như đối tượng ControllerInfo để bạn có thể xác định quyền kiểm soát quyền truy cập trên cơ sở từng yêu cầu.

Phát nội dung nghe nhìn trong nền

Để tiếp tục phát nội dung nghe nhìn khi ứng dụng không chạy ở nền trước, chẳng hạn như để phát nhạc, sách nói hoặc podcast ngay cả khi người dùng không mở ứng dụng, PlayerMediaSession của bạn phải được gói gọn trong một dịch vụ trên nền trước. Media3 cung cấp giao diện MediaSessionService cho mục đích này.

Triển khai MediaSessionService

Tạo một lớp mở rộng MediaSessionService và tạo thực thể MediaSession của bạn trong phương thức vòng đời onCreate().

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Java

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

Trong tệp kê khai, lớp Service có bộ lọc ý định MediaSessionService và yêu cầu quyền FOREGROUND_SERVICE để chạy dịch vụ trên nền trước:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Cuối cùng, trong lớp bạn đã tạo, hãy ghi đè phương thức onGetSession() để kiểm soát quyền truy cập của ứng dụng khách vào phiên phát nội dung đa phương tiện của bạn. Trả về một MediaSession để chấp nhận yêu cầu kết nối hoặc trả về null để từ chối yêu cầu.

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

Kết nối với giao diện người dùng

Giờ đây, phiên phát nội dung đa phương tiện của bạn nằm trong Service tách biệt với Activity hoặc Fragment nơi giao diện người dùng của trình phát, bạn có thể sử dụng MediaController để liên kết các phiên bản đó với nhau. Trong phương thức onStart() của Activity hoặc Fragment với giao diện người dùng của bạn, hãy tạo một SessionToken cho MediaSession, sau đó sử dụng SessionToken để tạo MediaController. Quá trình xây dựng MediaController diễn ra không đồng bộ.

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController triển khai giao diện Player, vì vậy, bạn có thể sử dụng các phương thức tương tự như play()pause() để điều khiển chế độ phát. Tương tự như các thành phần khác, hãy nhớ phát hành MediaController khi không cần thiết nữa, chẳng hạn như phương thức vòng đời onStop() của Activity, bằng cách gọi MediaController.releaseFuture().

Đăng thông báo

Bạn phải sử dụng dịch vụ trên nền trước để phát hành thông báo khi đang hoạt động. MediaSessionService sẽ tự động tạo thông báo MediaStyle cho bạn dưới dạng MediaNotification. Để cung cấp thông báo tuỳ chỉnh, hãy tạo MediaNotification.Provider với DefaultMediaNotificationProvider.Builder hoặc bằng cách tạo một phương thức triển khai tuỳ chỉnh giao diện của nhà cung cấp. Thêm trình cung cấp vào MediaSession bằng setMediaNotificationProvider.

Quảng cáo thư viện nội dung

MediaLibraryService được xây dựng dựa trên MediaSessionService bằng cách cho phép các ứng dụng khách duyệt qua nội dung nghe nhìn do ứng dụng của bạn cung cấp. Các ứng dụng khách sẽ triển khai một MediaBrowser để tương tác với MediaLibraryService của bạn.

Việc triển khai MediaLibraryService cũng tương tự như việc triển khai MediaSessionService, ngoại trừ việc trong onGetSession(), bạn nên trả về MediaLibrarySession thay vì MediaSession. So với MediaSession.Callback, MediaLibrarySession.Callback bao gồm các phương thức bổ sung cho phép ứng dụng trình duyệt di chuyển trong nội dung do dịch vụ thư viện của bạn cung cấp.

Tương tự như MediaSessionService, hãy khai báo MediaLibraryService trong tệp kê khai và yêu cầu quyền FOREGROUND_SERVICE để chạy dịch vụ trên nền trước:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Ví dụ ở trên bao gồm một bộ lọc ý định cho cả MediaLibraryServiceMediaBrowserService cũ để tương thích ngược. Bộ lọc ý định bổ sung cho phép các ứng dụng khách dùng API MediaBrowserCompat nhận ra Service của bạn.

MediaLibrarySession cho phép bạn phân phát thư viện nội dung của mình theo cấu trúc cây, với một MediaItem gốc duy nhất. Mỗi MediaItem trong cây có thể có số lượng nút con MediaItem bất kỳ. Bạn có thể phân phát một gốc khác hoặc một cây khác, tuỳ theo yêu cầu của ứng dụng. Ví dụ: cây mà bạn trả về cho một ứng dụng khách đang tìm danh sách các mục nội dung đa phương tiện được đề xuất có thể chỉ chứa MediaItem gốc và một cấp độ nút con MediaItem, trong khi cây mà bạn trả về một ứng dụng khách khác có thể đại diện cho một thư viện nội dung hoàn chỉnh hơn.

Đang tạo một MediaLibrarySession

MediaLibrarySession mở rộng API MediaSession để thêm các API duyệt nội dung. So với lệnh gọi lại MediaSession, lệnh gọi lại MediaLibrarySession sẽ thêm các phương thức như:

  • onGetLibraryRoot() khi ứng dụng yêu cầu MediaItem gốc của cây nội dung
  • onGetChildren() khi ứng dụng yêu cầu phần tử con của MediaItem trong cây nội dung
  • onGetSearchResult() khi một ứng dụng yêu cầu kết quả tìm kiếm từ cây nội dung cho một cụm từ tìm kiếm nhất định

Các phương thức gọi lại phù hợp sẽ bao gồm đối tượng LibraryParams có các tín hiệu bổ sung về loại cây nội dung mà ứng dụng khách quan tâm.