要在您的应用中使用 MediaRouter 框架,您必须获取一个 MediaRouter
对象的实例,并附加一个 MediaRouter.Callback
对象以监听路由事件。通过媒体路由发送的内容会经过该路由关联的 MediaRouteProvider
(少数特殊情况除外,例如蓝牙输出设备)。图 1 提供了用于在设备之间路由内容的类的高级视图。

图 1. 应用使用的关键媒体路由类概览。
注意:如果您的应用要支持 Google Cast 设备,您应该使用 Cast SDK 并将您的应用构建为 Cast 发送器。请遵循 Cast 文档中的说明,而不是直接使用 MediaRouter 框架。
媒体路由按钮
Android 应用应使用媒体路由按钮来控制媒体路由。MediaRouter 框架为该按钮提供了一个标准界面,有助于用户在可用时识别和使用路由功能。媒体路由按钮通常位于应用操作栏的右侧,如图 2 所示。

图 2. 操作栏中的媒体路由按钮。
当用户按下媒体路由按钮时,可用的媒体路由会以列表形式显示,如图 3 所示。

图 3. 按下媒体路由按钮后显示的可用媒体路由列表。
按照以下步骤创建媒体路由按钮
- 使用 AppCompatActivity
- 定义媒体路由按钮菜单项
- 创建 MediaRouteSelector
- 将媒体路由按钮添加到操作栏
- 在 activity 的生命周期中创建和管理 MediaRouter.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 是共享资源,您的应用需要响应常规 activity 生命周期回调来管理其 MediaRouter 回调
- 当 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
,并从 Service 的生命周期回调中调用媒体路由框架。
控制远程播放路由
当您选择远程播放路由时,您的应用将充当遥控器。路由另一端的设备处理所有内容数据检索、解码和播放功能。应用界面中的控件使用 RemotePlaybackClient
对象与接收设备通信。
RemotePlaybackClient
类提供了用于管理内容播放的其他方法。以下是 RemotePlaybackClient
类中的几个关键播放方法
play()
— 播放由Uri
指定的特定媒体文件。pause()
— 暂停当前播放的媒体轨道。resume()
— 在暂停命令后继续播放当前轨道。seek()
— 移动到当前轨道中的特定位置。release()
— 中断您的应用与远程播放设备之间的连接。
您可以使用这些方法将操作附加到您在应用中提供的播放控件。这些方法中的大多数还允许您包含一个回调对象,以便您可以监控播放任务或控制请求的进度。
RemotePlaybackClient
类还支持多个媒体项的播放排队和媒体队列的管理。
示例代码
Android BasicMediaRouter 和 MediaRouter 示例进一步演示了 MediaRouter API 的用法。