Phiên phát nội dung đa phương tiện cung cấp cách thức toàn cầu để tương tác với âm thanh hoặc video
trình phát. Trong Media3, trình phát mặc định là lớp ExoPlayer
, lớp này triển khai
giao diện Player
. Việc kết nối phiên nội dung nghe nhìn với trình phát sẽ cho phép ứng dụng
để quảng cáo việc phát nội dung nghe nhìn bên ngoài và nhận lệnh phát từ
nguồn bên ngoài.
Các lệnh có thể bắt nguồn từ các nút vật lý như nút phát trên tai nghe hoặc điều khiển từ xa TV. Chúng cũng có thể đến từ các ứng dụng khách có trình điều khiển nội dung nghe nhìn, chẳng hạn như hướng dẫn "tạm dừng" thành Trợ lý Google. Nội dung nghe nhìn phiên này uỷ quyền các lệnh này cho trình phát của ứng dụng đa phương tiện.
Thời điểm chọn một phiên phát nội dung nghe nhìn
Khi triển khai MediaSession
, bạn sẽ cho phép người dùng điều khiển chế độ phát:
- Thông qua tai nghe. Thường có các nút hoặc tương tác chạm người dùng có thể biểu diễn trên tai nghe để phát hoặc tạm dừng nội dung nghe nhìn hoặc chuyển đến hoặc bản nhạc trước đó.
- Bằng cách trò chuyện với Trợ lý Google. Một cách nói phổ biến là nói "OK Google, hãy tạm dừng" để tạm dừng mọi nội dung nghe nhìn đang phát trên thiết bị này.
- Thông qua đồng hồ Wear OS của họ. Điều này cho phép bạn dễ dàng truy cập vào các bộ điều khiển chế độ phát thông thường khi phát trên điện thoại.
- Thông qua mục Điều khiển nội dung nghe nhìn. Băng chuyền này hiển thị các chế độ điều khiển cho từng phiên nội dung nghe nhìn đang chạy.
- Trên TV. Cho phép thao tác bằng các nút phát vật lý, phát trên nền tảng điều khiển và quản lý nguồn điện (ví dụ: nếu TV, loa thanh hoặc bộ thu A/V tắt hoặc đầu vào được chuyển, thì quá trình phát sẽ dừng trong ứng dụng).
- Và bất kỳ quy trình bên ngoài nào khác cần ảnh hưởng đến quá trình phát.
Điều này rất hữu ích trong nhiều trường hợp sử dụng. Cụ thể, bạn nên cân nhắc
sử dụng MediaSession
khi:
- Bạn đang phát trực tuyến nội dung video dài, chẳng hạn như phim hoặc chương trình truyền hình trực tuyến.
- Bạn đang phát trực tuyến nội dung âm thanh dài, chẳng hạn như podcast hoặc nhạc danh sách phát.
- Bạn đang xây dựng một ứng dụng truyền hình.
Tuy nhiên, không phải trường hợp sử dụng nào cũng phù hợp với MediaSession
. Bạn nên
chỉ sử dụng Player
trong các trường hợp sau:
- Bạn đang hiển thị nội dung dạng ngắn nơi người dùng tương tác và tương tác với nhau là vô cùng quan trọng.
- Không có video nào đang hoạt động, chẳng hạn như người dùng đang cuộn qua một danh sách và nhiều video được hiển thị trên màn hình cùng một lúc.
- Bạn đang phát video giới thiệu hoặc giải thích một lần mà bạn mong muốn người dùng tích cực xem.
- Nội dung của bạn nhạy cảm về quyền riêng tư và bạn không muốn các quy trình bên ngoài truy cập siêu dữ liệu nội dung nghe nhìn (ví dụ: chế độ ẩn danh trong trình duyệt)
Nếu trường hợp sử dụng của bạn không thuộc bất kỳ trường hợp nào nêu trên, hãy cân nhắc xem bạn có
ứng dụng của bạn tiếp tục phát khi người dùng không tích cực tương tác
với nội dung. Nếu câu trả lời là có, bạn có thể muốn chọn
MediaSession
. Nếu câu trả lời là không, bạn có thể cần sử dụng Player
thay thế.
Tạo một phiên nội dung nghe nhìn
Một phiên nội dung đa phương tiện diễn ra cùng với trình phát mà trình phát đó quản lý. Bạn có thể tạo một
phiên phát nội dung nghe nhìn với đối tượng Context
và Player
. Bạn nên tạo và
khởi chạy một phiên nội dung nghe nhìn khi cần, chẳng hạn như onStart()
hoặc
Phương thức vòng đời onResume()
của Activity
, Fragment
hoặc onCreate()
của Service
sở hữu phiên nội dung đa phương tiện và trình phát được liên kết.
Để tạo một phiên phát nội dung đa phương tiện, hãy khởi chạy Player
và cung cấp phiên đó cho
MediaSession.Builder
thích điều này:
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();
Tự động xử lý trạng thái
Thư viện Media3 tự động cập nhật phiên phát nội dung nghe nhìn bằng cách sử dụng trạng thái của người chơi. Do đó, bạn không cần phải xử lý thủ công việc ánh xạ từ giữa các phiên.
Đây là điểm thay đổi so với cách tiếp cận cũ, trong đó bạn cần tạo và duy trì
PlaybackStateCompat
độc lập với chính trình phát, ví dụ:
chỉ ra bất kỳ lỗi nào.
Mã phiên duy nhất
Theo mặc định, MediaSession.Builder
sẽ tạo một phiên có chuỗi trống dưới dạng
mã phiên. Điều này là đủ nếu ứng dụng dự định chỉ tạo một
phiên bản thể thao. Đây là trường hợp phổ biến nhất.
Nếu một ứng dụng muốn quản lý nhiều phiên bản cùng lúc, thì ứng dụng
phải đảm bảo rằng mã nhận dạng phiên của mỗi phiên là duy nhất. Mã phiên có thể
khi tạo phiên bằng MediaSession.Builder.setId(String id)
.
Nếu bạn thấy IllegalStateException
gây ra sự cố cho ứng dụng của mình kèm theo lỗi
nhắn tin IllegalStateException: Session ID must be unique. ID=
thì nó sẽ là
có thể một phiên đã được tạo bất ngờ trước khi một phiên hoạt động được tạo trước đó
bản sao có cùng mã nhận dạng đã được huỷ bỏ. Để tránh các phiên bị rò rỉ bởi
lỗi lập trình, những trường hợp như vậy được phát hiện và thông báo bằng cách gửi một
ngoại lệ.
Cấp quyền kiểm soát cho các ứng dụng khách khác
Phiên phát nội dung đa phương tiện là chìa khoá để điều khiển quá trình phát. Định tuyến cho phép bạn định tuyến các lệnh từ nguồn bên ngoài gửi đến trình phát thực hiện công việc phát nội dung đa phương tiện. Các nguồn này có thể là các nút vật lý như nút phát trên tai nghe hoặc điều khiển từ xa TV hoặc lệnh gián tiếp chẳng hạn như hướng dẫn "tạm dừng" thành Trợ lý Google. Tương tự, bạn nên cấp quyền truy cập vào ứng dụng Android hệ thống hỗ trợ các chế độ kiểm soát thông báo và màn hình khoá hoặc cho Wear OS để bạn có thể điều khiển quá trình phát trên mặt đồng hồ. Khách hàng bên ngoài có thể sử dụng trình điều khiển nội dung nghe nhìn để ra lệnh phát cho ứng dụng đa phương tiện. Đây là mà phiên phát nội dung đa phương tiện của bạn nhận được, sau đó uỷ quyền truy cập các lệnh cho trình phát nội dung đa phương tiện.
![Sơ đồ minh hoạ sự tương tác giữa MediaSession và MediaController.](https://developer.android.com/static/guide/topics/media/images/backgroundcontrols.png?authuser=6&hl=vi)
Khi bộ điều khiển sắp kết nối với phiên phát nội dung nghe nhìn của bạn,
onConnect()
được gọi. Bạn có thể sử dụng ControllerInfo
được cung cấp
để quyết định có chấp nhận hay không
hoặc từ chối
yêu cầu. Xem ví dụ về cách chấp nhận yêu cầu kết nối trong phần Khai báo
các lệnh hiện có.
Sau khi kết nối, tay điều khiển có thể gửi lệnh phát cho phiên này. Chiến lược phát hành đĩa đơn
sau đó uỷ quyền các lệnh đó cho người chơi. Phát và danh sách phát
các lệnh được xác định trong giao diện Player
sẽ được tự động xử lý bởi
phiên hoạ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ỉnh và
sửa đổi danh sách phát).
Các lệnh gọi lại này cũng bao gồm đối tượng ControllerInfo
tương tự như vậy để bạn có thể sửa đổi
cách bạn phản hồi từng yêu cầu theo từng đơn vị kiểm soát.
Sửa đổi danh sách phát
Một phiên nội dung nghe nhìn có thể trực tiếp sửa đổi danh sách phát của trình phát như được giải thích trong
thời gian
Hướng dẫn dành cho danh sách phát của ExoPlayer.
Các đơn vị kiểm soát cũng có thể sửa đổi danh sách phát nếu
COMMAND_SET_MEDIA_ITEM
hoặc COMMAND_CHANGE_MEDIA_ITEMS
Bộ điều khiển có thể sử dụng.
Khi thêm mục mới vào danh sách phát, trình phát thường yêu cầu MediaItem
có
URI đã xác định
để làm cho chúng có thể phát được. Theo mặc định, các mặt hàng mới được thêm sẽ được chuyển tiếp tự động
vào các phương thức của trình phát như player.addMediaItem
nếu các phương thức đó có URI được xác định.
Nếu muốn tuỳ chỉnh các thực thể MediaItem
được thêm vào trình phát, bạn có thể
ghi đè
onAddMediaItems()
.
Đây là bước cần thiết khi bạn muốn hỗ trợ các bộ điều khiển yêu cầu nội dung đa phương tiện
mà không có URI được xác định. Thay vào đó, MediaItem
thường có
một hoặc nhiều trường sau đây được đặt để mô tả nội dung nghe nhìn được yêu cầu:
MediaItem.id
: Mã nhận dạng chung cho nội dung nghe nhìn.MediaItem.RequestMetadata.mediaUri
: URI yêu cầu có thể sử dụng một tuỳ chỉnh và không nhất thiết phải mà người chơi trực tiếp chơi được.MediaItem.RequestMetadata.searchQuery
: Truy vấn tìm kiếm văn bản, chẳng hạn như từ Trợ lý Google.MediaItem.MediaMetadata
: Siêu dữ liệu có cấu trúc, chẳng hạn như "tiêu đề" hoặc "nghệ sĩ".
Để có thêm lựa chọn tuỳ chỉnh cho danh sách phát hoàn toàn mới, bạn có thể
ghi đè bổ sung
onSetMediaItems()
cho phép bạn xác định mục bắt đầu và vị trí trong danh sách phát. Ví dụ:
bạn có thể mở rộng một mục được yêu cầu cho toàn bộ danh sách phát và hướng dẫn
trình phát để bắt đầu tại chỉ mục của mục được yêu cầu ban đầu. Đáp
mẫu triển khai onSetMediaItems()
có tính năng này trong ứng dụng minh hoạ phiên hoạt động.
Quản lý bố cục tuỳ chỉnh và lệnh tuỳ chỉnh
Các phần sau đây mô tả cách quảng cáo bố cục tuỳ chỉnh của các nút lệnh cho ứng dụng khách và cho phép bộ điều khiển gửi các lệnh.
Xác định bố cục tuỳ chỉnh của phiên
Để cho các ứng dụng khách biết bạn muốn hiển thị bộ điều khiển chế độ phát nào
hãy đặt bố cục tuỳ chỉnh của phiên
khi tạo MediaSession
trong phương thức onCreate()
của
.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
Khai báo trình phát có sẵn và lệnh tuỳ chỉnh
Ứng dụng đa phương tiện có thể xác định các lệnh tuỳ chỉnh có thể dùng trong
một bố cục tuỳ chỉnh. Ví dụ: bạn có thể muốn triển khai các nút cho phép
để lưu một mục nội dung đa phương tiện vào danh sách mục yêu thích. MediaController
sẽ gửi lệnh tuỳ chỉnh và MediaSession.Callback
sẽ nhận các lệnh đó.
Bạn có thể xác định những lệnh trong phiên tuỳ chỉnh nào dùng được cho
MediaController
khi thiết bị này kết nối với phiên phát nội dung nghe nhìn của bạn. Bạn đạt được điều này bằng cách
ghi đè MediaSession.Callback.onConnect()
. Định cấu hình và trả về
tập hợp các lệnh có sẵn khi chấp nhận yêu cầu kết nối từ
MediaController
trong phương thức gọi lại onConnect
:
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
Để nhận các yêu cầu lệnh tuỳ chỉnh từ MediaController
, hãy ghi đè phương thức
Phương thức onCustomCommand()
trong Callback
.
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
Bạn có thể theo dõi xem trình điều khiển nội dung nghe nhìn nào đang đưa ra yêu cầu bằng cách sử dụng
Thuộc tính packageName
của đối tượng MediaSession.ControllerInfo
được truyền vào phương thức Callback
. Điều này cho phép bạn điều chỉnh
để phản hồi một lệnh nhất định nếu lệnh đó bắt nguồn từ hệ thống,
ứng dụng riêng hoặc các ứng dụng khách khác.
Cập nhật bố cục tuỳ chỉnh sau khi người dùng tương tác
Sau khi xử lý một lệnh tuỳ chỉnh hoặc bất kỳ tương tác nào khác với người chơi, bạn
có thể muốn cập nhật bố cục hiển thị trong giao diện người dùng trình điều khiển. Ví dụ điển hình
là nút bật tắt thay đổi biểu tượng sau khi kích hoạt hành động được liên kết
bằng nút này. Để cập nhật bố cục, bạn có thể sử dụng
MediaSession.setCustomLayout
:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
Tuỳ chỉnh hành vi của lệnh phát
Cách tuỳ chỉnh hành vi của một lệnh được xác định trong giao diện Player
, chẳng hạn như
dưới dạng play()
hoặc seekToNext()
, hãy gói Player
trong một ForwardingPlayer
.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Để biết thêm thông tin về ForwardingPlayer
, hãy xem hướng dẫn về ExoPlayer trên
Tuỳ chỉnh.
Xác định trình điều khiển yêu cầu của một lệnh người chơi
Khi lệnh gọi đến phương thức Player
do MediaController
bắt nguồn, bạn có thể
xác định nguồn gốc xuất xứ bằng MediaSession.controllerForCurrentRequest
và nhận ControllerInfo
cho yêu cầu hiện tại:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
Nút phản hồi với nội dung nghe nhìn
Nút nội dung đa phương tiện là các nút phần cứng có trên thiết bị Android và thiết bị ngoại vi khác
, chẳng hạn như nút phát/tạm dừng trên tai nghe Bluetooth. Xử lý Media3
cho bạn khi họ đến phiên và gọi
phương thức Player
thích hợp trên trình phát trong phiên.
Một ứng dụng có thể ghi đè hành vi mặc định bằng cách ghi đè
MediaSession.Callback.onMediaButtonEvent(Intent)
. Trong trường hợp như vậy, ứng dụng
có thể/cần tự xử lý tất cả chi tiết API cụ thể.
Xử lý và báo cáo lỗi
Có 2 loại lỗi mà một phiên phát ra và báo cáo cho bộ điều khiển. Lỗi nghiêm trọng báo cáo lỗi phát lại kỹ thuật của phiên trình phát làm gián đoạn quá trình phát. Các lỗi nghiêm trọng được báo cáo cho bộ điều khiển tự động khi chúng xảy ra. Lỗi không nghiêm trọng là lỗi không liên quan đến kỹ thuật hoặc chính sách không làm gián đoạn quá trình phát và được gửi đến bộ điều khiển bằng ứng dụng theo cách thủ công.
Lỗi phát lại nghiêm trọng
Lỗi phát lại nghiêm trọng được trình phát báo cáo cho phiên, sau đó
đã báo cáo cho bộ điều khiển để gọi qua
Player.Listener.onPlayerError(PlaybackException)
và
Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException)
Trong trường hợp như vậy, trạng thái phát sẽ chuyển đổi thành STATE_IDLE
và
MediaController.getPlaybackError()
trả về PlaybackException
đã gây ra
chuyển đổi. Bộ điều khiển có thể kiểm tra PlayerException.errorCode
để biết
thông tin về lý do xảy ra lỗi.
Để có khả năng tương tác, một lỗi nghiêm trọng sẽ được sao chép vào PlaybackStateCompat
của phiên hoạt động trên nền tảng bằng cách chuyển trạng thái sang STATE_ERROR
và cài đặt
mã lỗi và thông báo theo PlaybackException
.
Tuỳ chỉnh lỗi nghiêm trọng
Để cung cấp thông tin có ý nghĩa và được bản địa hoá cho người dùng, mã lỗi,
và thông báo lỗi bổ sung của lỗi phát nghiêm trọng có thể được tuỳ chỉnh bằng
bằng cách sử dụng ForwardingPlayer
khi tạo phiên:
Kotlin
val forwardingPlayer = ErrorForwardingPlayer(player) val session = MediaSession.Builder(context, forwardingPlayer).build()
Java
Player forwardingPlayer = new ErrorForwardingPlayer(player); MediaSession session = new MediaSession.Builder(context, forwardingPlayer).build();
Trình phát chuyển tiếp đăng ký Player.Listener
cho người chơi thực
và chặn các lệnh gọi lại báo cáo lỗi. Một
Sau đó, PlaybackException
được uỷ quyền cho các trình nghe
được đăng ký trên trình phát chuyển tiếp. Để làm được điều này, trình phát chuyển tiếp
ghi đè Player.addListener
và Player.removeListener
để có quyền truy cập vào
trình nghe để gửi mã lỗi, thông báo hoặc dữ liệu bổ sung tuỳ chỉnh:
Kotlin
class ErrorForwardingPlayer(private val context: Context, player: Player) : ForwardingPlayer(player) { private val listeners: MutableList<Player.Listener> = mutableListOf() private var customizedPlaybackException: PlaybackException? = null init { player.addListener(ErrorCustomizationListener()) } override fun addListener(listener: Player.Listener) { listeners.add(listener) } override fun removeListener(listener: Player.Listener) { listeners.remove(listener) } override fun getPlayerError(): PlaybackException? { return customizedPlaybackException } private inner class ErrorCustomizationListener : Player.Listener { override fun onPlayerErrorChanged(error: PlaybackException?) { customizedPlaybackException = error?.let { customizePlaybackException(it) } listeners.forEach { it.onPlayerErrorChanged(customizedPlaybackException) } } override fun onPlayerError(error: PlaybackException) { listeners.forEach { it.onPlayerError(customizedPlaybackException!!) } } private fun customizePlaybackException( error: PlaybackException, ): PlaybackException { val buttonLabel: String val errorMessage: String when (error.errorCode) { PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> { buttonLabel = context.getString(R.string.err_button_label_restart_stream) errorMessage = context.getString(R.string.err_msg_behind_live_window) } // Apps can customize further error messages by adding more branches. else -> { buttonLabel = context.getString(R.string.err_button_label_ok) errorMessage = context.getString(R.string.err_message_default) } } val extras = Bundle() extras.putString("button_label", buttonLabel) return PlaybackException(errorMessage, error.cause, error.errorCode, extras) } override fun onEvents(player: Player, events: Player.Events) { listeners.forEach { it.onEvents(player, events) } } // Delegate all other callbacks to all listeners without changing arguments like onEvents. } }
Java
private static class ErrorForwardingPlayer extends ForwardingPlayer { private final Context context; private List<Player.Listener> listeners; @Nullable private PlaybackException customizedPlaybackException; public ErrorForwardingPlayer(Context context, Player player) { super(player); this.context = context; listeners = new ArrayList<>(); player.addListener(new ErrorCustomizationListener()); } @Override public void addListener(Player.Listener listener) { listeners.add(listener); } @Override public void removeListener(Player.Listener listener) { listeners.remove(listener); } @Nullable @Override public PlaybackException getPlayerError() { return customizedPlaybackException; } private class ErrorCustomizationListener implements Listener { @Override public void onPlayerErrorChanged(@Nullable PlaybackException error) { customizedPlaybackException = error != null ? customizePlaybackException(error, context) : null; for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onPlayerErrorChanged(customizedPlaybackException); } } @Override public void onPlayerError(PlaybackException error) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onPlayerError(checkNotNull(customizedPlaybackException)); } } private PlaybackException customizePlaybackException( PlaybackException error, Context context) { String buttonLabel; String errorMessage; switch (error.errorCode) { case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW: buttonLabel = context.getString(R.string.err_button_label_restart_stream); errorMessage = context.getString(R.string.err_msg_behind_live_window); break; // Apps can customize further error messages by adding more case statements. default: buttonLabel = context.getString(R.string.err_button_label_ok); errorMessage = context.getString(R.string.err_message_default); break; } Bundle extras = new Bundle(); extras.putString("button_label", buttonLabel); return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras); } @Override public void onEvents(Player player, Events events) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onEvents(player, events); } } // Delegate all other callbacks to all listeners without changing arguments like onEvents. } }
Lỗi không nghiêm trọng
Hệ thống có thể gửi các lỗi không nghiêm trọng không bắt nguồn từ một trường hợp ngoại lệ về kỹ thuật bởi một ứng dụng cho tất cả hoặc cho một đơn vị kiểm soát cụ thể:
Kotlin
val sessionError = SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired), ) // Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError) // Interoperability: Sending a nonfatal error to the media notification controller to set the // error code and error message in the playback state of the platform media session. mediaSession.mediaNotificationControllerInfo?.let { mediaSession.sendError(it, sessionError) }
Java
SessionError sessionError = new SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired)); // Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError); // Interoperability: Sending a nonfatal error to the media notification controller to set the // error code and error message in the playback state of the platform media session. ControllerInfo mediaNotificationControllerInfo = mediaSession.getMediaNotificationControllerInfo(); if (mediaNotificationControllerInfo != null) { mediaSession.sendError(mediaNotificationControllerInfo, sessionError); }
Một lỗi không nghiêm trọng gửi đến trình điều khiển thông báo nội dung nghe nhìn sẽ được sao chép vào
PlaybackStateCompat
phiên hoạt động trên nền tảng. Do đó, chỉ có mã lỗi và
thông báo lỗi sẽ được đặt thành PlaybackStateCompat
tương ứng, trong khi
PlaybackStateCompat.state
chưa được thay đổi thành STATE_ERROR
.
Nhận lỗi không nghiêm trọng
MediaController
gặp lỗi không nghiêm trọng khi triển khai
MediaController.Listener.onError
:
Kotlin
val future = MediaController.Builder(context, sessionToken) .setListener(object : MediaController.Listener { override fun onError(controller: MediaController, sessionError: SessionError) { // Handle nonfatal error. } }) .buildAsync()
Java
MediaController.Builder future = new MediaController.Builder(context, sessionToken) .setListener( new MediaController.Listener() { @Override public void onError(MediaController controller, SessionError sessionError) { // Handle nonfatal error. } });