为了在您的应用中使用 MediaRouter 框架,您必须获取 MediaRouter
对象的实例,并附加一个 MediaRouter.Callback
对象来监听路由事件。通过媒体路由发送的内容会通过路由关联的 MediaRouteProvider
传递(少数特殊情况除外,例如蓝牙输出设备)。图 1 提供了用于在设备之间路由内容的类的概述。
注意:如果您希望您的应用支持 Google Cast 设备,则应使用 Cast SDK 并将您的应用构建为 Cast 发送方。请遵循 Cast 文档 中的说明,而不是直接使用 MediaRouter 框架。
媒体路由按钮
Android 应用应使用媒体路由按钮来控制媒体路由。MediaRouter 框架为按钮提供了一个标准接口,这有助于用户识别和使用可用的路由。媒体路由按钮通常放置在应用操作栏的右侧,如图 2 所示。
当用户按下媒体路由按钮时,可用的媒体路由将显示在一个列表中,如图 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 创建时调用 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,并在其隐藏 (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(); } ... }
MediaRouter 框架还提供了一个 MediaRouteDiscoveryFragment
类,该类负责为 activity 添加和删除回调。
注意:如果您正在编写音乐播放应用,并希望该应用在后台播放音乐,则必须构建一个用于播放的 Service
,并从 Service 的生命周期回调中调用媒体路由器框架。
控制远程播放路由
选择远程播放路由时,您的应用充当遥控器。路由另一端的设备处理所有内容数据检索、解码和播放功能。您应用 UI 中的控件使用 RemotePlaybackClient
对象与接收设备通信。
RemotePlaybackClient
类提供了用于管理内容播放的其他方法。以下是 RemotePlaybackClient
类中一些关键的播放方法:
play()
— 播放特定媒体文件,由Uri
指定。pause()
— 暂停当前播放的媒体曲目。resume()
— 在暂停命令后继续播放当前曲目。seek()
— 移动到当前曲目中的特定位置。release()
— 拆除应用与远程播放设备之间的连接。
您可以使用这些方法将操作附加到您在应用中提供的播放控件。这些方法中的大多数还允许您包含回调对象,以便您可以监视播放任务或控制请求的进度。
RemotePlaybackClient
类还支持对多个媒体项目进行排队播放和媒体队列管理。
示例代码
Android BasicMediaRouter 和 MediaRouter 示例进一步演示了 MediaRouter API 的用法。