Hướng dẫn di chuyển AndroidX Media3

Các ứng dụng hiện đang sử dụng com.google.android.exoplayer2 thư viện độc lập và androidx.media nên di chuyển sang androidx.media3. Sử dụng tập lệnh di chuyển để di chuyển các tệp bản dựng Gradle, tệp nguồn Java và Kotlin, cũng như tệp bố cục XML từ ExoPlayer 2.19.1 sang AndroidX Media3 1.1.1.

Tổng quan

Trước khi di chuyển, hãy xem các phần sau để tìm hiểu thêm về lợi ích của các API mới, API cần di chuyển và điều kiện tiên quyết mà dự án của ứng dụng phải đáp ứng.

Tại sao nên di chuyển sang Jetpack Media3

  • Đây là nơi mới của ExoPlayer, trong khi com.google.android.exoplayer2 đã ngừng hoạt động.
  • Truy cập vào Player API trên các thành phần/quy trình bằng MediaBrowser/MediaController.
  • Sử dụng các chức năng mở rộng của API vàMediaSessionMediaController
  • Quảng cáo các chức năng phát bằng quyền kiểm soát truy cập chi tiết.
  • Đơn giản hoá ứng dụng bằng cách xoá MediaSessionConnectorPlayerNotificationManager.
  • Tương thích ngược với các API ứng dụng tương thích với nội dung nghe nhìn (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

API nội dung nghe nhìn cần di chuyển sang AndroidX Media3

  • ExoPlayer và các tiện ích của ExoPlayer
    Điều này bao gồm tất cả các mô-đun của dự án ExoPlayer cũ, ngoại trừ mô-đun mediasession đã ngừng hoạt động. Bạn có thể di chuyển các ứng dụng hoặc mô-đun tuỳ thuộc vào các gói trong com.google.android.exoplayer2 bằng tập lệnh di chuyển.
  • MediaSessionConnector (tuỳ thuộc vào các gói androidx.media.* của androidx.media:media:1.4.3+)
    Xoá MediaSessionConnector và sử dụng androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (tuỳ thuộc vào các gói androidx.media.* của androidx.media:media:1.4.3+)
    Di chuyển các lớp con của androidx.media.MediaBrowserServiceCompat sang androidx.media3.session.MediaLibraryService và mã sử dụng MediaBrowserCompat.MediaItem sang androidx.media3.common.MediaItem.
  • MediaBrowserCompat (tuỳ thuộc vào các gói android.support.v4.media.* của androidx.media:media:1.4.3+)
    Di chuyển mã ứng dụng sử dụng MediaBrowserCompat hoặc MediaControllerCompat để sử dụng androidx.media3.session.MediaBrowser với androidx.media3.common.MediaItem.

Điều kiện tiên quyết

  1. Đảm bảo dự án của bạn đang được kiểm soát nguồn

    Đảm bảo bạn có thể dễ dàng hoàn nguyên các thay đổi do các công cụ di chuyển bằng tập lệnh áp dụng. Nếu bạn chưa kiểm soát nguồn dự án của mình, thì bây giờ là thời điểm thích hợp để bắt đầu. Nếu vì lý do nào đó mà bạn không muốn làm như vậy, hãy tạo bản sao lưu dự án trước khi bắt đầu di chuyển.

  2. Cập nhật ứng dụng

    • Bạn nên cập nhật dự án để sử dụng phiên bản mới nhất của thư viện ExoPlayer và xoá mọi lệnh gọi đến các phương thức không dùng nữa. Nếu bạn dự định sử dụng tập lệnh để di chuyển, thì bạn cần phải khớp phiên bản mà bạn đang cập nhật với phiên bản do tập lệnh xử lý.

    • Tăng compileSdkVersion của ứng dụng lên ít nhất là 32.

    • Nâng cấp Gradle và trình bổ trợ Android Studio cho Gradle lên phiên bản gần đây hoạt động với các phần phụ thuộc đã cập nhật ở trên. Ví dụ:

      • Phiên bản trình bổ trợ Android cho Gradle: 7.1.0
      • Phiên bản Gradle: 7.4
    • Thay thế tất cả câu lệnh nhập ký tự đại diện đang sử dụng dấu hoa thị (*) và sử dụng câu lệnh nhập đủ điều kiện: Xoá câu lệnh nhập ký tự đại diện và sử dụng Android Studio để nhập câu lệnh đủ điều kiện (F2 – Alt/Enter, F2 – Alt/Enter, ...).

    • Di chuyển từ com.google.android.exoplayer2.PlayerView sang com.google.android.exoplayer2.StyledPlayerView. Điều này là cần thiết vì không có com.google.android.exoplayer2.PlayerView tương đương trong AndroidX Media3.

Di chuyển ExoPlayer có hỗ trợ tập lệnh

Tập lệnh giúp di chuyển từ com.google.android.exoplayer2 sang cấu trúc mô-đun và gói mới trong androidx.media3. Tập lệnh áp dụng một số bước kiểm tra xác thực cho dự án của bạn và in cảnh báo nếu xác thực không thành công. Nếu không, tập lệnh sẽ áp dụng các mối liên kết của các lớp và gói được đổi tên trong tài nguyên của một dự án Gradle Android được viết bằng Java hoặc Kotlin.

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

Sử dụng tập lệnh di chuyển

  1. Tải xuống tập lệnh di chuyển từ thẻ của dự án ExoPlayer trên GitHub tương ứng với phiên bản mà bạn đã cập nhật ứng dụng:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. Đặt tập lệnh có thể thực thi:

    chmod 744 media3-migration.sh
    
  3. Chạy tập lệnh bằng --help để tìm hiểu về các tuỳ chọn.

  4. Chạy tập lệnh bằng -l để liệt kê tập hợp các tệp được chọn để di chuyển (sử dụng -f để buộc liệt kê mà không có cảnh báo):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. Chạy tập lệnh bằng -m để liên kết các gói, lớp và mô-đun với Media3. Việc chạy tập lệnh bằng tuỳ chọn -m sẽ áp dụng các thay đổi cho các tệp đã chọn.

    • Dừng ở lỗi xác thực mà không thay đổi
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • Thực thi bắt buộc

    Nếu tập lệnh phát hiện thấy vi phạm điều kiện tiên quyết, thì bạn có thể buộc di chuyển bằng cờ -f:

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

Hoàn tất các bước thủ công sau khi chạy tập lệnh bằng tuỳ chọn -m:

  1. Kiểm tra cách tập lệnh thay đổi mã của bạn: Sử dụng công cụ so sánh và khắc phục các vấn đề tiềm ẩn (hãy cân nhắc gửi báo cáo lỗi nếu bạn cho rằng tập lệnh có vấn đề chung được giới thiệu mà không truyền tuỳ chọn -f).
  2. Tạo dự án: Sử dụng ./gradlew clean build hoặc trong Android Studio, hãy chọn File > Sync Project with Gradle Files (Tệp > Đồng bộ hoá dự án với các tệp Gradle), sau đó chọn Build > Clean project (Xây dựng > Dọn dẹp dự án) rồi chọn Build > Rebuild project (Xây dựng > Xây dựng lại dự án) (theo dõi bản dựng trong thẻ 'Build - Build Output' (Bản dựng – Đầu ra bản dựng) của Android Studio).

Các bước tiếp theo nên thực hiện:

  1. Giải quyết lựa chọn tham gia đối với các lỗi liên quan đến việc sử dụng API không ổn định.
  2. Thay thế các lệnh gọi API không dùng nữa: Sử dụng API thay thế được đề xuất. Giữ con trỏ trên cảnh báo trong Android Studio và tham khảo JavaDoc của ký hiệu không dùng nữa để tìm hiểu xem nên sử dụng gì thay cho một lệnh gọi nhất định.
  3. Sắp xếp các câu lệnh nhập: Mở dự án trong Android Studio, sau đó nhấp chuột phải vào một nút thư mục gói trong trình xem dự án và chọn Optimize imports (Tối ưu hoá lệnh nhập) trên các gói chứa tệp nguồn đã thay đổi.

Thay thế MediaSessionConnector bằng androidx.media3.session.MediaSession

Trong thế giới MediaSessionCompat cũ, MediaSessionConnector chịu trách nhiệm đồng bộ hoá trạng thái của trình phát với trạng thái của phiên và nhận lệnh từ các bộ điều khiển cần uỷ quyền cho các phương thức trình phát thích hợp. Với AndroidX Media3, việc này được thực hiện trực tiếp bởi MediaSession mà không cần trình kết nối.

  1. Xoá tất cả các tham chiếu và việc sử dụng MediaSessionConnector: Nếu bạn đã sử dụng tập lệnh tự động để di chuyển các lớp và gói ExoPlayer, thì tập lệnh có thể đã để lại mã của bạn ở trạng thái không thể biên dịch liên quan đến MediaSessionConnector không thể giải quyết. Android Studio sẽ hiển thị mã bị hỏng khi bạn cố gắng tạo hoặc khởi động ứng dụng.

  2. Trong tệp build.gradle nơi bạn duy trì các phần phụ thuộc, hãy thêm phần phụ thuộc triển khai vào mô-đun phiên AndroidX Media3 và xoá phần phụ thuộc cũ:

    implementation "androidx.media3:media3-session:1.10.0"
    
  3. Thay thế MediaSessionCompat bằng androidx.media3.session.MediaSession.

  4. Tại vị trí mã nơi bạn đã tạo MediaSessionCompat cũ, hãy sử dụng androidx.media3.session.MediaSession.Builder để tạo MediaSession. Truyền trình phát để tạo trình tạo phiên.

    Kotlin

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

    Java

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

  5. Triển khai MySessionCallback theo yêu cầu của ứng dụng. Đây là bước không bắt buộc. Nếu bạn muốn cho phép bộ điều khiển thêm mục nội dung nghe nhìn vào trình phát, hãy triển khai MediaSession.Callback.onAddMediaItems(). Phương thức này phục vụ nhiều phương thức API hiện tại và cũ, thêm các mục nội dung nghe nhìn vào trình phát để phát theo cách tương thích ngược. Điều này bao gồm các phương thức MediaController.set/addMediaItems() của bộ điều khiển Media3, cũng như các phương thức TransportControls.prepareFrom*/playFrom* của API cũ. Bạn có thể tìm thấy một ví dụ về cách triển khai onAddMediaItems trong PlaybackService của ứng dụng minh họa phiên bản demo.

  6. Hủy phiên đa phương tiện tại vị trí mã nơi bạn đã hủy phiên trước khi di chuyển:

    Kotlin

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }

    Java

    if (mediaSession != null) {
      mediaSession.getPlayer().release();
      mediaSession.release();
      mediaSession = null;
    }

Chức năng MediaSessionConnector trong Media3

Bảng sau đây cho thấy các API Media3 xử lý chức năng được triển khai trước đó trong MediaSessionConnector.

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() được gọi nội bộ)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

Di chuyển MediaBrowserService sang MediaLibraryService

AndroidX Media3 giới thiệu MediaLibraryService thay thế MediaBrowserServiceCompat. JavaDoc của MediaLibraryService và siêu lớp MediaSessionService cung cấp phần giới thiệu hay về API và mô hình lập trình không đồng bộ của dịch vụ.

MediaLibraryService tương thích ngược với MediaBrowserService. Ứng dụng ứng dụng đang sử dụng MediaBrowserCompat hoặc MediaControllerCompat tiếp tục hoạt động mà không cần thay đổi mã khi kết nối với MediaLibraryService. Đối với ứng dụng, việc ứng dụng của bạn đang sử dụng MediaLibraryService hay MediaBrowserServiceCompat cũ là điều minh bạch.

Sơ đồ thành phần ứng dụng có dịch vụ, hoạt động và ứng dụng bên ngoài.
Hình 1: Tổng quan về thành phần ứng dụng đa phương tiện
  1. Để khả năng tương thích ngược hoạt động, bạn cần đăng ký cả hai giao diện dịch vụ với dịch vụ của mình trong AndroidManifest.xml. Bằng cách này, ứng dụng sẽ tìm thấy dịch vụ của bạn theo giao diện dịch vụ bắt buộc:

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. Trong tệp build.gradle nơi bạn duy trì các phần phụ thuộc, hãy thêm phần phụ thuộc triển khai vào mô-đun phiên AndroidX Media3 và xoá phần phụ thuộc cũ:

    implementation "androidx.media3:media3-session:1.10.0"
    
  3. Thay đổi dịch vụ của bạn để kế thừa từ MediaLibraryService thay vì MediaBrowserService Như đã nói trước đó, MediaLibraryService tương thích với MediaBrowserService cũ. Theo đó, API rộng hơn mà dịch vụ đang cung cấp cho ứng dụng vẫn giống nhau. Vì vậy, có khả năng là một ứng dụng có thể giữ lại hầu hết logic cần thiết để triển khai MediaBrowserService và điều chỉnh logic đó cho MediaLibraryService mới.

    Những điểm khác biệt chính so với MediaBrowserServiceCompat cũ như sau:

    • Triển khai các phương thức vòng đời dịch vụ: Các phương thức cần được ghi đè trên chính dịch vụ là onCreate/onDestroy, nơi ứng dụng phân bổ/phát hành phiên thư viện, trình phát và các tài nguyên khác. Ngoài các phương thức vòng đời dịch vụ tiêu chuẩn, ứng dụng cần ghi đè onGetSession(MediaSession.ControllerInfo) để trả về MediaLibrarySession được tạo trong onCreate.

    • Triển khai MediaLibraryService.MediaLibrarySessionCallback: Việc tạo phiên yêu cầu MediaLibraryService.MediaLibrarySessionCallback triển khai các phương thức API miền thực tế. Vì vậy, thay vì ghi đè các phương thức API của dịch vụ cũ, bạn sẽ ghi đè các phương thức của MediaLibrarySession.Callback.

      Sau đó, lệnh gọi lại được dùng để tạo MediaLibrarySession:

      Kotlin

      mediaLibrarySession = MediaLibrarySession.Builder(context, player, MySessionCallback()).build()

      Java

      mediaLibrarySession =
          new MediaLibrarySession.Builder(context, player, new MySessionCallback()).build();

      Tìm API đầy đủ của MediaLibrarySessionCallback trong tài liệu API.

    • Triển khai MediaSession.Callback.onAddMediaItems(): Lệnh gọi lại onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) phục vụ nhiều phương thức API hiện tại và cũ, thêm các mục nội dung nghe nhìn vào trình phát để phát theo cách tương thích ngược. Điều này bao gồm các phương thức MediaController.set/addMediaItems() của bộ điều khiển Media3, cũng như các phương thức TransportControls.prepareFrom*/playFrom* của API cũ. Bạn có thể tìm thấy một ví dụ về cách triển khai lệnh gọi lại có thể tìm thấy trong PlaybackService của ứng dụng demo phiên.

    • AndroidX Media3 đang sử dụng androidx.media3.common.MediaItem thay vì MediaBrowserCompat.MediaItemMediaMetadataCompat. Các phần mã của bạn được liên kết với các lớp cũ cần được thay đổi cho phù hợp hoặc liên kết với MediaItem Media3.

    • Mô hình lập trình không đồng bộ chung đã thay đổi thành Futures trái ngược với phương thức Result có thể tách rời của MediaBrowserServiceCompat. Việc triển khai dịch vụ của bạn có thể trả về một không đồng bộ ListenableFuture thay vì tách rời kết quả hoặc trả về một Future tức thì để trực tiếp trả về một giá trị.

Xoá PlayerNotificationManager

MediaLibraryService tự động hỗ trợ thông báo nội dung nghe nhìn và bạn có thể xoá PlayerNotificationManager khi sử dụng MediaLibraryService hoặc MediaSessionService.

Ứng dụng có thể tuỳ chỉnh thông báo bằng cách đặt MediaNotification.Provider tuỳ chỉnh trong onCreate() thay thế DefaultMediaNotificationProvider. Sau đó, MediaLibraryService sẽ lo việc bắt đầu dịch vụ ở nền trước theo yêu cầu.

Bằng cách ghi đè MediaLibraryService.updateNotification(), ứng dụng có thể tiếp tục nắm toàn quyền sở hữu việc đăng thông báo và bắt đầu/dừng dịch vụ ở nền trước theo yêu cầu.

Di chuyển mã ứng dụng bằng MediaBrowser

Với AndroidX Media3, MediaBrowser triển khai các giao diện MediaController/Player và có thể dùng để kiểm soát việc phát nội dung nghe nhìn bên cạnh việc duyệt thư viện nội dung nghe nhìn. Nếu bạn phải tạo MediaBrowserCompat và a MediaControllerCompat trong thế giới cũ, thì bạn có thể thực hiện tương tự bằng cách chỉ sử dụng the MediaBrowser trong Media3.

Bạn có thể tạo MediaBrowser và chờ kết nối với dịch vụ được thiết lập:

Kotlin

scope.launch {
  val sessionToken = SessionToken(context, ComponentName(context, "MusicService"))
  browser =
    MediaBrowser.Builder(context, sessionToken)
      .setListener(BrowserListener())
      .buildAsync()
      .await()
}

Java

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, "MusicService"));
ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken)
        .setListener(new BrowserListener())
        .buildAsync();

Hãy xem Kiểm soát việc phát trong phiên đa phương tiện để tìm hiểu cách tạo MediaController nhằm kiểm soát việc phát ở chế độ nền.

Các bước tiếp theo và dọn dẹp

Lỗi API không ổn định

Sau khi di chuyển sang Media3, bạn có thể thấy các lỗi lint về việc sử dụng API không ổn định. Bạn có thể sử dụng các API này một cách an toàn và các lỗi tìm lỗi mã nguồn là sản phẩm phụ của các đảm bảo mới về khả năng tương thích nhị phân. Nếu không yêu cầu khả năng tương thích nhị phân nghiêm ngặt, bạn có thể chặn các lỗi này một cách an toàn bằng chú giải @OptIn.

Nền

Cả ExoPlayer v1 và v2 đều không đưa ra các đảm bảo nghiêm ngặt về khả năng tương thích nhị phân của thư viện giữa các phiên bản tiếp theo. Nền tảng ExoPlayer API được thiết kế rất lớn để cho phép các ứng dụng tuỳ chỉnh hầu hết mọi khía cạnh của việc phát. Các phiên bản tiếp theo của ExoPlayer đôi khi sẽ giới thiệu việc đổi tên ký hiệu hoặc các thay đổi mang tính đột phá khác (ví dụ: các phương thức bắt buộc mới trên giao diện). Trong hầu hết các trường hợp, những sự cố này đã được giảm thiểu bằng cách giới thiệu ký hiệu mới cùng với việc ngừng sử dụng ký hiệu cũ trong một vài phiên bản, để cho phép nhà phát triển có thời gian di chuyển việc sử dụng của họ, nhưng điều này không phải lúc nào cũng có thể thực hiện được.

Những thay đổi mang tính đột phá này đã gây ra 2 vấn đề cho người dùng thư viện ExoPlayer v1 và v2:

  1. Việc nâng cấp từ lên phiên bản ExoPlayer có thể khiến mã ngừng biên dịch.
  2. Một ứng dụng phụ thuộc vào ExoPlayer cả trực tiếp và thông qua một thư viện trung gian phải đảm bảo rằng cả hai phần phụ thuộc đều là cùng một phiên bản, nếu không, sự không tương thích nhị phân có thể dẫn đến sự cố trong thời gian chạy.

Những điểm cải tiến trong Media3

Media3 đảm bảo khả năng tương thích nhị phân cho một tập hợp con của nền tảng API. Các phần không đảm bảo khả năng tương thích nhị phân được đánh dấu bằng @UnstableApi. Để làm rõ sự khác biệt này, việc sử dụng các ký hiệu API không ổn định sẽ tạo ra lỗi lint trừ phi chúng được chú thích bằng @OptIn.

Sau khi di chuyển từ ExoPlayer v2 sang Media3, bạn có thể thấy nhiều lỗi lint API không ổn định. Điều này có thể khiến Media3 có vẻ "kém ổn định" hơn ExoPlayer v2. Không phải như vậy. Các phần "không ổn định" của API Media3 có cùng mức độ ổn định như toàn bộ nền tảng ExoPlayer v2 API và các đảm bảo của nền tảng Media3 API ổn định hoàn toàn không có trong ExoPlayer v2. Sự khác biệt đơn giản là lỗi lint hiện cảnh báo bạn về các mức độ ổn định khác nhau.

Xử lý lỗi lint API không ổn định

Hãy xem phần khắc phục sự cố về các lỗi lint này để biết thông tin chi tiết về cách chú thích việc sử dụng API không ổn định của Java và Kotlin bằng @OptIn.

API không dùng nữa

Bạn có thể nhận thấy rằng các lệnh gọi đến API không dùng nữa bị gạch ngang trong Android Studio. Bạn nên thay thế các lệnh gọi như vậy bằng phương án thay thế thích hợp. Di chuột qua ký hiệu để xem JavaDoc cho biết API nào cần sử dụng thay thế.

Ảnh chụp màn hình: Cách hiển thị JavaDoc bằng phương thức thay thế cho phương thức không dùng nữa
Hình 3: Chú giải công cụ JavaDoc trong Android Studio đề xuất một phương án thay thế cho mọi ký hiệu không dùng nữa.

Mã mẫu và ứng dụng minh hoạ