若要在您的应用中使用 MediaRouter 框架,您必须获取 MediaRouter
对象的实例,并附加 MediaRouter.Callback
对象以监听路由事件。
通过媒体路由发送的内容会通过该路由的关联 MediaRouteProvider
(少数特殊情况除外,例如蓝牙输出设备)。图 1 简要介绍了用于在设备之间路由内容的类。
![](https://developer.android.google.cn/static/images/mediarouter/mediarouter-framework.png?authuser=6&hl=sv)
图 1. 应用使用的关键媒体路由器类概览。
注意:如果您希望应用支持 Google Cast 设备,则应使用 Cast SDK 并将您的应用构建为 Cast 发送器。请按照 Cast 文档中的说明操作,而不要直接使用 MediaRouter 框架。
媒体路由按钮
Android 应用应使用媒体路由按钮来控制媒体路由。MediaRouter 框架为该按钮提供了一个标准接口,帮助用户识别和使用路由(如果有)。媒体路由按钮通常位于应用操作栏的右侧,如图 2 所示。
![](https://developer.android.google.cn/static/images/mediarouter/mediarouter-actionbar.png?authuser=6&hl=sv)
图 2. 操作栏中的媒体路由按钮。
当用户按下媒体路由按钮时,可用的媒体路由会显示在列表中,如图 3 所示。
![](https://developer.android.google.cn/static/images/mediarouter/mediarouter-selector-ui.png?authuser=6&hl=sv)
图 3. 按媒体路由按钮后显示的可用媒体路由列表。
要创建媒体路由按钮,请按以下步骤操作:
- 使用 AppCompatActivity
- 定义媒体路由按钮菜单项
- 创建 MediaRouteSelector
- 将媒体路由按钮添加到操作栏
- 在 Activity 的生命周期内创建并管理 MediaRouter.Callback 方法
本部分介绍了前四个步骤。下一部分介绍了 Callback 方法。
使用 AppCompatActivity
在 activity 中使用媒体路由器框架时,您应从 AppCompatActivity
扩展 activity 并导入软件包 androidx.appcompat.app
。您必须将 androidx.appcompat:appcompat 和 androidx.mediarouter:mediarouter 支持库添加到应用开发项目中。如需详细了解如何向项目添加支持库,请参阅 Android Jetpack 使用入门。
注意:请务必使用媒体路由器框架的 androidx
实现。请勿使用旧版 android.media
软件包。
定义媒体路由按钮菜单项
创建一个为媒体路由按钮定义菜单项的 XML 文件。该项的操作应为 MediaRouteActionProvider
类。下面是一个示例文件:
// myMediaRouteButtonMenuItem.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="@+id/media_route_menu_item" android:title="@string/media_route_menu_title" app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider" app:showAsAction="always" /> </menu>
创建 MediaRouteSelector
媒体路由按钮菜单中显示的路由由 MediaRouteSelector
确定。从 AppCompatActivity
扩展您的 activity,并在创建 activity 时从 onCreate() 方法调用 MediaRouteSelector.Builder
来构建选择器,如以下代码示例所示。请注意,选择器保存在类变量中,并且允许的路由类型是通过添加 MediaControlIntent
对象来指定的:
Kotlin
class MediaRouterPlaybackActivity : AppCompatActivity() { private var mSelector: MediaRouteSelector? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create a route selector for the type of routes your app supports. mSelector = MediaRouteSelector.Builder() // These are the framework-supported intents .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .build() } }
Java
public class MediaRouterPlaybackActivity extends AppCompatActivity { private MediaRouteSelector mSelector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create a route selector for the type of routes your app supports. mSelector = new MediaRouteSelector.Builder() // These are the framework-supported intents .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .build(); } }
对于大多数应用而言,唯一需要的路由类型是 CATEGORY_REMOTE_PLAYBACK
。此路由类型会将运行您的应用的设备视为遥控器。连接的接收方设备会处理所有内容数据的检索、解码和播放。支持 Google Cast 的应用(例如 Chromecast)的运作方式就是这样。
少数制造商支持称为“辅助输出”的特殊路由选项。借助此路由,您的媒体应用可检索、渲染视频或音乐,并直接将视频或音乐流式传输到所选远程接收端设备上的屏幕和/或扬声器。使用辅助输出将内容发送到支持无线功能的音乐系统或视频显示屏。如需启用发现和选择这些设备的功能,您需要将 CATEGORY_LIVE_AUDIO
或 CATEGORY_LIVE_VIDEO
控件类别添加到 MediaRouteSelector 中。您还需要创建和处理自己的 Presentation
对话框。
将媒体路由按钮添加到操作栏
定义媒体路由菜单和 MediaRouteSelector 后,您现在可以将媒体路由按钮添加到 activity。为每个 activity 替换 onCreateOptionsMenu()
方法以添加选项菜单。
Kotlin
override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) // Inflate the menu and configure the media router action provider. menuInflater.inflate(R.menu.sample_media_router_menu, menu) // Attach the MediaRouteSelector to the menu item val mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item) val mediaRouteActionProvider = MenuItemCompat.getActionProvider(mediaRouteMenuItem) as MediaRouteActionProvider // Attach the MediaRouteSelector that you built in onCreate() selector?.also(mediaRouteActionProvider::setRouteSelector) // Return true to show the menu. return true }
Java
@Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); // Inflate the menu and configure the media router action provider. getMenuInflater().inflate(R.menu.sample_media_router_menu, menu); // Attach the MediaRouteSelector to the menu item MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item); MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider)MenuItemCompat.getActionProvider( mediaRouteMenuItem); // Attach the MediaRouteSelector that you built in onCreate() mediaRouteActionProvider.setRouteSelector(selector); // Return true to show the menu. return true; }
如需详细了解如何在应用中实现操作栏,请参阅操作栏开发者指南。
您还可以在任何视图中将媒体路由按钮添加为 MediaRouteButton
。您必须使用 setRouteSelector()
方法将 MediaRouteSelector 附加到该按钮。有关将媒体路由按钮集成到应用中的指南,请参阅 Google Cast 设计核对清单。
MediaRouter 回调
在同一设备上运行的所有应用共享一个 MediaRouter
实例及其路由(按应用的 MediaRouteSelector 按应用过滤)。每个 activity 均使用自己的 MediaRouter.Callback
方法实现与 MediaRouter 进行通信。每当用户选择、更改路由或断开路由时,MediaRouter 都会调用回调方法。
您可以替换回调中的多个方法,以接收有关路由事件的信息。MediaRouter.Callback
类的实现至少应替换 onRouteSelected()
和 onRouteUnselected()
。
由于 MediaRouter 是共享资源,因此您的应用需要管理其 MediaRouter 回调,以响应常规的 activity 生命周期回调:
- 创建 Activity (
onCreate(Bundle)
) 后,抓取指向MediaRouter
的指针,并在应用的生命周期内持有它。 - 当 activity 变为可见 (
onStart()
) 时,将回调附加到 MediaRouter,并在 activity 隐藏 (onStop()
) 时分离它们。
以下代码示例演示了如何创建和保存回调对象、如何获取 MediaRouter
的实例,以及如何管理回调。
请注意,在 onStart()
中附加回调时使用 CALLBACK_FLAG_REQUEST_DISCOVERY
标志。这样,MediaRouteSelector 便可以刷新媒体路由按钮的可用路由列表。
Kotlin
class MediaRouterPlaybackActivity : AppCompatActivity() { private var mediaRouter: MediaRouter? = null private var mSelector: MediaRouteSelector? = null // Variables to hold the currently selected route and its playback client private var mRoute: MediaRouter.RouteInfo? = null private var remotePlaybackClient: RemotePlaybackClient? = null // Define the Callback object and its methods, save the object in a class variable private val mediaRouterCallback = object : MediaRouter.Callback() { override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) { Log.d(TAG, "onRouteSelected: route=$route") if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { // Stop local playback (if necessary) // ... // Save the new route mRoute = route // Attach a new playback client remotePlaybackClient = RemotePlaybackClient(this@MediaRouterPlaybackActivity, mRoute) // Start remote playback (if necessary) // ... } } override fun onRouteUnselected( router: MediaRouter, route: MediaRouter.RouteInfo, reason: Int ) { Log.d(TAG, "onRouteUnselected: route=$route") if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { // Changed route: tear down previous client mRoute?.also { remotePlaybackClient?.release() remotePlaybackClient = null } // Save the new route mRoute = route when (reason) { MediaRouter.UNSELECT_REASON_ROUTE_CHANGED -> { // Resume local playback (if necessary) // ... } } } } } // Retain a pointer to the MediaRouter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Get the media router service. mediaRouter = MediaRouter.getInstance(this) ... } // Use this callback to run your MediaRouteSelector to generate the // list of available media routes override fun onStart() { mSelector?.also { selector -> mediaRouter?.addCallback(selector, mediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY) } super.onStart() } // Remove the selector on stop to tell the media router that it no longer // needs to discover routes for your app. override fun onStop() { mediaRouter?.removeCallback(mediaRouterCallback) super.onStop() } ... }
Java
public class MediaRouterPlaybackActivity extends AppCompatActivity { private MediaRouter mediaRouter; private MediaRouteSelector mSelector; // Variables to hold the currently selected route and its playback client private MediaRouter.RouteInfo mRoute; private RemotePlaybackClient remotePlaybackClient; // Define the Callback object and its methods, save the object in a class variable private final MediaRouter.Callback mediaRouterCallback = new MediaRouter.Callback() { @Override public void onRouteSelected(MediaRouter router, RouteInfo route) { Log.d(TAG, "onRouteSelected: route=" + route); if (route.supportsControlCategory( MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ // Stop local playback (if necessary) // ... // Save the new route mRoute = route; // Attach a new playback client remotePlaybackClient = new RemotePlaybackClient(this, mRoute); // Start remote playback (if necessary) // ... } } @Override public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) { Log.d(TAG, "onRouteUnselected: route=" + route); if (route.supportsControlCategory( MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ // Changed route: tear down previous client if (mRoute != null && remotePlaybackClient != null) { remotePlaybackClient.release(); remotePlaybackClient = null; } // Save the new route mRoute = route; if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { // Resume local playback (if necessary) // ... } } } } // Retain a pointer to the MediaRouter @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Get the media router service. mediaRouter = MediaRouter.getInstance(this); ... } // Use this callback to run your MediaRouteSelector to generate the list of available media routes @Override public void onStart() { mediaRouter.addCallback(mSelector, mediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); super.onStart(); } // Remove the selector on stop to tell the media router that it no longer // needs to discover routes for your app. @Override public void onStop() { mediaRouter.removeCallback(mediaRouterCallback); super.onStop(); } ... }
媒体路由器框架还提供了一个 MediaRouteDiscoveryFragment
类,该类负责添加和移除 activity 的回调。
注意:如果您正在编写音乐播放应用并希望应用在后台时播放音乐,则必须构建用于播放的 Service
,并从服务的生命周期回调中调用媒体路由器框架。
控制远程播放路由
在选择远程播放路由时,您的应用会充当遥控器。路线另一端的设备会处理所有内容数据的检索、解码和播放功能。应用界面中的控件使用 RemotePlaybackClient
对象与接收方设备进行通信。
RemotePlaybackClient
类提供了其他用于管理内容播放的方法。以下是一些来自 RemotePlaybackClient
类的主要播放方法:
play()
- 播放由Uri
指定的特定媒体文件。pause()
- 暂停当前正在播放的媒体轨道。resume()
- 执行暂停命令后继续播放当前曲目。seek()
- 移至当前轨道中的特定位置。release()
- 断开应用与远程播放设备的连接。
您可以使用这些方法将操作附加到您在应用中提供的播放控件。其中大多数方法还允许您添加回调对象,以便您可以监控播放任务或控件请求的进度。
RemotePlaybackClient
类还支持将多个媒体项加入队列,以便播放和管理媒体队列。
示例代码
Android BasicMediaRouter 和 MediaRouter 示例进一步演示了如何使用 MediaRouter API。