Google 致力于为黑人社区推动种族平等。查看具体举措

新的媒体控件

Android 11 更新了媒体控件的显示方式,使用 MediaSessionMediaRouter2 API 呈现控件和音频输出信息。

Android 11 中的媒体控件位于“快捷设置”旁。来自多个应用的会话排列在一个可滑动的轮播界面中。轮播界面按以下顺序列出会话:

  • 在手机本地播放的会话流
  • 远程会话流,例如在外部设备上检测到的会话或投射会话
  • 可继续播放的以前的会话(按上次播放的顺序排列)

用户无需启动相关应用即可在轮播界面中重新开始播放以前的会话。当播放开始后,用户可按常规方式与媒体控件互动。

支持“继续播放媒体内容”功能

为了使用此功能,您必须在“开发者选项”设置中启用继续播放媒体内容

如需让您的播放器应用显示在快捷设置区域,您必须创建包含有效 MediaSession 令牌的 MediaStyle 通知。

如需显示媒体播放器的品牌图标,请使用 NotificationBuilder.setSmallIcon()

如需支持“继续播放媒体内容”功能,应用必须实现 MediaBrowserServiceMediaSession

MediaBrowserService 实现

设备启动后,系统会查找最近使用过的五个媒体应用,并提供可用于在每个应用中重新开始播放媒体内容的控件。

系统会尝试通过来自 SystemUI 的连接与您的 MediaBrowserService 联系。您的应用必须允许此类连接,否则无法支持“继续播放媒体内容”功能。

您可以使用软件包名称 com.android.systemui 和签名来标识和验证来自 SystemUI 的连接。SystemUI 使用平台签名进行签名。您可以在 UAMP 应用中找到有关如何检查平台签名的示例。

为了支持“继续播放媒体内容”功能,MediaBrowserService 必须实现以下行为:

  • onGetRoot() 必须快速返回非 null 的根。其他复杂逻辑应在 onLoadChildren() 中处理

  • 对根媒体 ID 调用 onLoadChildren() 时,结果必须包含 FLAG_PLAYABLE 子项。

  • 收到 EXTRA_RECENT 查询时,MediaBrowserService 应返回最近播放的媒体项。返回的值应为实际媒体项而非通用函数。

  • MediaBrowserService 必须提供适当的 MediaDescription 以及非空标题副标题。它还应设置一个图标 URI图标位图

以下代码示例说明了如何实现 onGetRoot()

Kotlin

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? {
    ...
    // Verify that the specified package is SystemUI. You'll need to write your
    // own logic to do this.
    if (isSystem(clientPackageName, clientUid)) {
        rootHints?.let {
            if (it.getBoolean(BrowserRoot.EXTRA_RECENT)) {
                // Return a tree with a single playable media item for resumption.
                return BrowserRoot(MY_RECENTS_ROOT_ID, null)
            }
        }
        // You can return your normal tree if the EXTRA_RECENT flag is not present.
        return BrowserRoot(MY_MEDIA_ROOT_ID, null)
    }
    // Return an empty tree to disallow browsing.
    return BrowserRoot(MY_EMPTY_ROOT_ID, null)

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {
    ...
    // Verify that the specified package is SystemUI. You'll need to write your
    // own logic to do this.
    if (isSystem(clientPackageName, clientUid)) {
        if (rootHints != null) {
            if (rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) {
                // Return a tree with a single playable media item for resumption.
                return new BrowserRoot(MY_RECENTS_ROOT_ID, null);
            }
        }
        // You can return your normal tree if the EXTRA_RECENT flag is not present.
        return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
    }
    // Return an empty tree to disallow browsing.
    return new BrowserRoot(MY_EMPTY_ROOT_ID, null);
}

MediaSession 实现

系统会从 MediaSessionMediaMetadata 中检索以下信息,并在其可用时显示这些信息:

  • METADATA_KEY_ALBUM_ART_URI
  • METADATA_KEY_TITLE
  • METADATA_KEY_ARTIST
  • METADATA_KEY_DURATION(如果未设置时长,进度条不会显示进度)

为了支持“继续播放媒体内容”功能,MediaSession 必须实现对 onPlay()MediaSession 回调。

媒体播放器会显示当前正在播放的媒体内容的已播时间,以及映射到 MediaSession PlaybackState 的进度条。

为了使进度条正常工作,您必须实现 PlaybackState.Builder#setActions 并包含 ACTION_SEEK_TO。否则,播放器将仅显示已播时间和时长。

如需设置播放器控件,请使用 Notification.Builder#setCustomActions。如果媒体播放器出现在收起的快捷设置中,该媒体播放器只会显示通过 Notification.MediaStyle#setShowsActionsInCompactView 指示的操作。