Para usar o framework do MediaRouter no seu app, você precisa acessar uma instância
do objeto MediaRouter
e anexar um
objeto MediaRouter.Callback
para detectar eventos de roteamento.
O conteúdo enviado por um roteamento de mídia passa pelo MediaRouteProvider
associado ao trajeto (exceto em alguns casos especiais, como um dispositivo de saída Bluetooth). A Figura 1 mostra uma visualização de alto nível das classes usadas para rotear conteúdo entre dispositivos.
Observação:se você quiser que seu app seja compatível com dispositivos Google Cast, use o SDK do Cast e crie seu app como um remetente do Cast. Siga as instruções na documentação do Cast em vez de usar o framework do MediaRouter diretamente.
Botão de roteamento de mídia
Apps para Android devem usar um botão de roteamento de mídia para fins de controle. O framework do MediaRouter fornece uma interface padrão para o botão, o que ajuda os usuários a reconhecer e usar o roteamento quando ele estiver disponível. O botão de roteamento de mídia geralmente é colocado no lado direito da barra de ações do app, como mostrado na Figura 2.
Quando o usuário pressiona o botão de roteamento de mídia, os roteamentos disponíveis aparecem em uma lista, como mostrado na Figura 3.
Siga estas etapas para criar um botão de roteamento de mídia:
- Use uma AppCompatActivity.
- Defina o item de menu do botão de roteamento de mídia.
- Crie um MediaRouteSelector.
- Adicione o botão de roteamento de mídia à barra de ações.
- Crie e processe os métodos MediaRouter.Callback no ciclo de vida da sua atividade.
Essa seção descreve as quatro primeiras etapas. A seção seguinte descreve os métodos de callback.
Use uma AppCompatActivity.
Ao usar o framework do roteador de mídia em uma atividade, estenda
a atividade de AppCompatActivity
e importe o
pacote androidx.appcompat.app
. É preciso adicionar as bibliotecas de suporte
androidx.appcompat:appcompat
e androidx.mediarouter:mediarouter
ao projeto de desenvolvimento do app. Para mais informações sobre como adicionar bibliotecas de suporte
ao seu projeto, consulte Introdução ao Android Jetpack.
Cuidado:use a implementação androidx
do framework do roteador de mídia. Não use o pacote android.media
mais antigo.
Definir o item de menu do botão de roteamento de mídia
Crie um arquivo XML que defina um item menu para o botão de rota de mídia.
A ação do item deve ser a classe MediaRouteActionProvider
.
Veja um exemplo de arquivo:
// 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>
Criar um MediaRouteSelector
Os roteamentos que aparecem no menu do botão de roteamento de mídia são determinados por um MediaRouteSelector
.
Estenda a atividade do AppCompatActivity
e crie o seletor quando a atividade for criada, chamando MediaRouteSelector.Builder
do método onCreate(), conforme mostrado
no exemplo de código a seguir. O seletor é salvo em uma variável de classe, e os tipos de trajeto permitidos são especificados adicionando objetos 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(); } }
Para a maioria dos aplicativos, o único tipo de trajeto necessário é CATEGORY_REMOTE_PLAYBACK
. Esse tipo de roteamento trata o dispositivo que está executando seu app como um controle remoto.
O dispositivo receptor conectado processa toda a recuperação, decodificação e reprodução dos dados de conteúdo.
É assim que apps compatíveis com o Google Cast, como o
Chromecast, funcionam.
Alguns fabricantes oferecem compatibilidade com uma opção de roteamento especial conhecida como "saída secundária". Com esse roteamento, seu
app de música recupera, renderiza e transmite vídeos ou músicas diretamente para a tela e/ou os alto-falantes do dispositivo receptor remoto selecionado.
Use a saída secundária para enviar conteúdo para sistemas de música ou telas de vídeo sem fio. Para ativar a descoberta e
a seleção desses dispositivos, é necessário adicionar as categorias de controle
CATEGORY_LIVE_AUDIO
ou
CATEGORY_LIVE_VIDEO
ao MediaRouteSelector. Também é necessário criar e processar a própria caixa de diálogo Presentation
.
Adicionar o botão de roteamento de mídia à barra de ações
Com o menu de roteamento de mídia e o MediaRouteSelector definidos, é possível adicionar o botão de roteamento de mídia a uma atividade.
Modifique o método onCreateOptionsMenu()
em cada uma das atividades para adicionar um menu
de opções.
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; }
Para saber mais sobre como implementar a barra de ações no seu app, consulte o guia para desenvolvedores sobre Barra de ações.
Também é possível adicionar um botão de roteamento de mídia como um MediaRouteButton
em qualquer
visualização. Você precisa anexar um MediaRouteSelector ao botão por meio do método setRouteSelector()
. Consulte a Lista de verificação de design do Google Cast para ver diretrizes sobre como incorporar o botão de roteamento de mídia no seu app.
Callbacks do MediaRouter
Todos os apps em execução no mesmo dispositivo compartilham uma única instância de MediaRouter
e as rotas
dela (filtradas por app pelo MediaRouteSelector do app). Cada atividade se comunica com o MediaRouter
usando a própria implementação de métodos
MediaRouter.Callback
. O MediaRouter chama os métodos de callback sempre que o usuário seleciona, muda ou desconecta um roteamento.
Há vários métodos no callback que você pode modificar para receber informações sobre eventos de roteamento. No mínimo, sua implementação da classe MediaRouter.Callback
precisa modificar
onRouteSelected()
e
onRouteUnselected()
.
Como o MediaRouter é um recurso compartilhado, seu app precisa gerenciar os callbacks do MediaRouter em resposta aos callbacks comuns do ciclo de vida da atividade:
- Quando a atividade for criada (
onCreate(Bundle)
), pegue um ponteiro paraMediaRouter
e segure-o por todo o ciclo de vida do app. - Anexe callbacks ao MediaRouter quando a atividade se tornar visível (
onStart()
) e remova-os quando ela estiver oculta (onStop()
).
O exemplo de código a seguir demonstra como
criar e salvar o objeto de callback, como
receber uma instância de MediaRouter
e como gerenciar callbacks.
Observe o uso da sinalização CALLBACK_FLAG_REQUEST_DISCOVERY
ao anexar os callbacks em onStart()
.
Isso permite que o MediaRouteSelector atualize a lista de rotas disponíveis do botão
de roteamento de mídia.
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(); } ... }
O framework do roteador de mídia também oferece uma classe MediaRouteDiscoveryFragment
, que se encarrega de adicionar e remover o callback de uma atividade.
Observação:se você estiver criando um app de reprodução de música e quiser que ele toque
música em segundo plano, crie um Service
para reprodução
e chame o framework do roteador de mídia usando os callbacks do ciclo de vida do serviço.
Controlar um roteamento de reprodução remota
Quando você seleciona um roteamento de reprodução remota, seu app funciona como um controle remoto. O dispositivo na outra extremidade do trajeto processa todas as funções de recuperação, decodificação e reprodução de dados de conteúdo. Os controles na interface do seu app se comunicam com o dispositivo receptor usando um objeto RemotePlaybackClient
.
A classe RemotePlaybackClient
oferece outros métodos para gerenciar a reprodução de conteúdo. Veja alguns dos principais métodos de reprodução da classe RemotePlaybackClient
:
play()
: reproduz um arquivo de mídia específico, especificado por umaUri
.pause()
: pausa a faixa de mídia em reprodução no momento.resume()
: continua tocando a faixa atual após um comando de pausa.seek()
: move para uma posição específica na faixa atual.release()
: encerra a conexão do app com o dispositivo de reprodução remota.
Você pode usar esses métodos para anexar ações aos controles de reprodução oferecidos no app. A maioria desses métodos também permite incluir um objeto de callback para monitorar o progresso da tarefa de reprodução ou da solicitação de controle.
A classe RemotePlaybackClient
também é compatível com o enfileiramento de vários itens de mídia para reprodução e gerenciamento da fila.
Exemplo de código
As amostras BasicMediaRouter do Android e MediaRouter (links em inglês) demonstram em mais detalhes o uso da API MediaRouter.