Чтобы использовать фреймворк MediaRouter в приложении, необходимо получить экземпляр объекта MediaRouter и прикрепить объект MediaRouter.Callback для прослушивания событий маршрутизации.
Контент, отправленный по маршруту мультимедиа, проходит через связанный с ним объект MediaRouteProvider (за исключением некоторых особых случаев, например при использовании устройства вывода Bluetooth). На рисунке 1 показаны классы, используемые для маршрутизации контента между устройствами.
Рисунок 1. Обзор основных классов маршрутизатора медиа, используемых приложениями.
Примечание. Если вы хотите, чтобы ваше приложение поддерживало устройства Google Cast, используйте Cast SDK и создайте приложение в качестве отправителя Cast. Следуйте инструкциям в документации по Cast, а не используйте фреймворк MediaRouter напрямую.
Кнопка маршрутизации медиаконтента
В приложениях для Android для управления маршрутизацией медиаконтента должна использоваться кнопка маршрутизации медиаконтента. Фреймворк MediaRouter предоставляет стандартный интерфейс для кнопки, который помогает пользователям распознавать и использовать маршрутизацию, когда она доступна. Кнопка Media Route обычно размещается в правой части панели действий приложения, как показано на рисунке 2.
Рисунок 2. Кнопка маршрутизации медиаконтента на панели действий.
Когда пользователь нажимает кнопку маршрута медиа, доступные маршруты медиа отображаются в виде списка, как показано на рисунке 3.
Рисунок 3. Список доступных маршрутов медиа, который показывается после нажатия кнопки маршрута медиа.
Чтобы создать кнопку маршрута медиа, выполните следующие действия:
- Используйте AppCompatActivity
- Определите пункт меню кнопки маршрутизации медиаконтента
- Создание MediaRouteSelector
- Добавьте кнопку маршрутизации медиаданных на панель действий
- Создание методов MediaRouter.Callback и управление ими в жизненном цикле объекта activity
В этом разделе описаны первые четыре шага. В следующем разделе описываются методы обратного вызова.
Используйте AppCompatActivity
При использовании фреймворка MediaRouter в действии необходимо расширить действие из AppCompatActivity и импортировать пакет androidx.appcompat.app. Вам необходимо добавить в проект разработки приложения библиотеки поддержки androidx.appcompat:appcompat и androidx.mediarouter:mediarouter. Подробнее о том, как добавить библиотеки поддержки в проект, рассказывается в статье Начало работы с Android Jetpack.
Внимание! Обязательно используйте реализацию фреймворка Media Router из библиотеки 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.
Расширьте свой класс activity, унаследовав его от AppCompatActivity, и создайте селектор при создании activity, вызвав MediaRouteSelector.Builder из метода onCreate(), как показано в примере кода ниже. Обратите внимание, что селектор сохраняется в переменной класса, а допустимые типы маршрутов указываются путем добавления объектов 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, вы можете добавить кнопку маршрута медиа в действие.
Переопределите метод 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 в любом представлении. Вы должны прикрепить MediaRouteSelector к кнопке с помощью метода setRouteSelector(). Рекомендации по добавлению кнопки выбора маршрута медиаданных в приложение можно найти в контрольном списке по дизайну Google Cast.
Обратные вызовы MediaRouter
Все приложения, запущенные на одном устройстве, используют один экземпляр MediaRouter и его маршруты (отфильтрованные для каждого приложения с помощью MediaRouteSelector). Каждое действие взаимодействует с MediaRouter, используя собственную реализацию методов MediaRouter.Callback. MediaRouter вызывает методы обратного вызова, когда пользователь выбирает, меняет или отключает маршрут.
В обратном вызове есть несколько методов, которые можно переопределить, чтобы получать информацию о событиях маршрутизации. Как минимум, ваша реализация класса MediaRouter.Callback должна переопределять onRouteSelected() и onRouteUnselected().
Поскольку MediaRouter является общим ресурсом, ваше приложение должно управлять обратными вызовами MediaRouter в ответ на обычные обратные вызовы жизненного цикла объекта activity.
- При создании действия (
onCreate(Bundle)) получите указатель наMediaRouterи сохраните его на все время работы приложения. - Прикрепите обратные вызовы к MediaRouter, когда действие становится видимым (
onStart()), и отсоедините их, когда оно скрыто (onStop()).
В приведенном ниже примере кода показано, как создать и сохранить объект обратного вызова, как получить экземпляр MediaRouter и как управлять обратными вызовами.
Обратите внимание на использование флага CALLBACK_FLAG_REQUEST_DISCOVERY при прикреплении обратных вызовов в onStart().
Это позволит 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(); } ... }
Фреймворк Media Router также предоставляет класс MediaRouteDiscoveryFragment, который отвечает за добавление и удаление обратного вызова для действия.
Примечание. Если вы разрабатываете приложение для воспроизведения музыки и хотите, чтобы оно работало в фоновом режиме, вам необходимо создать Service для воспроизведения и вызвать фреймворк MediaRouter из обратных вызовов жизненного цикла службы.
Управление удаленным маршрутом воспроизведения
Когда вы выбираете удаленный маршрут воспроизведения, ваше приложение действует как пульт дистанционного управления. Устройство на другом конце маршрута выполняет все функции по извлечению, декодированию и воспроизведению контента. Элементы управления в интерфейсе приложения взаимодействуют с устройством-приемником с помощью объекта RemotePlaybackClient.
Класс RemotePlaybackClient предоставляет дополнительные методы для управления воспроизведением контента. Вот несколько основных методов воспроизведения из класса RemotePlaybackClient:
play()– воспроизвести определенный медиафайл, указанный вUri.pause()– приостановить воспроизведение.resume()– продолжить воспроизведение текущего трека после паузы.seek()– перейти к определенному моменту в текущем треке.release()– разорвать соединение между приложением и устройством удаленного воспроизведения.
Эти методы позволяют привязать действия к элементам управления воспроизведением в приложении. Большинство из них также позволяют включить объект обратного вызова, чтобы вы могли отслеживать ход выполнения задачи воспроизведения или запроса на управление.
Класс RemotePlaybackClient также поддерживает постановку в очередь нескольких мультимедийных объектов для воспроизведения и управление очередью.
Пример кода
Примеры использования MediaRouter API можно найти в разделах Android BasicMediaRouter и MediaRouter.