Omówienie MediaRouter

Aby korzystać z platformy MediaRouter w aplikacji, musisz uzyskać instancję obiektu MediaRouter i dołączyć obiekt MediaRouter.Callback do nasłuchiwania zdarzeń routingu. Treści wysyłane przez trasę multimediów przechodzą przez powiązany z nią element MediaRouteProvider (z wyjątkiem kilku szczególnych przypadków, np. przez urządzenie wyjściowe Bluetooth). Rysunek 1 przedstawia ogólny widok klas używanych do kierowania treści między urządzeniami.

Rysunek 1. Omówienie najważniejszych klas routerów multimediów używanych przez aplikacje.

Uwaga: jeśli chcesz, aby aplikacja obsługiwała urządzenia Google Cast, skorzystaj z pakietu SDK Cast i utwórz aplikację jako nadawca. Postępuj zgodnie z instrukcjami podanymi w dokumentacji przesyłania zamiast bezpośrednio używać platformy MediaRouter.

Przycisk kierowania multimediów

Aplikacje na Androida powinny używać przycisku trasy multimediów do sterowania routingiem multimediów. Platforma MediaRouter zapewnia standardowy interfejs przycisku, który ułatwia użytkownikom rozpoznawanie dostępnego routingu i korzystanie z niego. Przycisk trasy multimediów znajduje się zwykle po prawej stronie paska działań aplikacji, jak widać na ilustracji 2.

Rysunek 2. na pasku działań.

Gdy użytkownik kliknie przycisk trasy multimediów, dostępne trasy multimediów zostaną wyświetlone na liście, tak jak to widać na ilustracji 3.

Rysunek 3. Lista dostępnych tras multimediów wyświetlana po naciśnięciu przycisku kierowania multimediów.

Aby utworzyć przycisk trasy multimediów, wykonaj te czynności:

  1. Używanie obiektu AppCompatActivity
  2. Zdefiniuj element menu Zdefiniuj element menu kierowania multimediów
  3. Tworzenie obiektu MediaRouteSelector
  4. Dodawanie przycisku kierowania multimediów do paska działań
  5. Tworzenie metod MediaRouter.Callback w cyklu życia aktywności i zarządzanie nimi

W tej sekcji opisujemy cztery pierwsze kroki. W następnej sekcji opisujemy metody wywołania zwrotnego.

Używanie obiektu AppCompatActivity

Jeśli w działaniu używasz platformy routera multimediów, musisz rozszerzyć działanie z AppCompatActivity i zaimportować pakiet androidx.appcompat.app. Do projektu, w którym się znajdujesz, musisz dodać biblioteki obsługi androidx.appcompat:appcompat i androidx.mediarouter:mediarouter. Więcej informacji o dodawaniu bibliotek pomocy do projektu znajdziesz w artykule Pierwsze kroki z Androidem Jetpack.

Uwaga: pamiętaj, aby użyć implementacji platformy routera multimediów androidx. Nie używaj starszego pakietu android.media.

Utwórz plik XML definiujący pozycję menu przycisku trasy multimediów. Akcją elementu powinna być klasa MediaRouteActionProvider. Oto przykładowy plik:

// 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>

Tworzenie obiektu MediaRouteSelector

Trasy widoczne w menu przycisku trasy multimediów są określane znakiem MediaRouteSelector. Rozszerz aktywność z AppCompatActivity i utwórz selektor po utworzeniu aktywności wywołującej metodę MediaRouteSelector.Builder z metody onCreate() jak w przykładzie poniżej. Pamiętaj, że selektor jest zapisany w zmiennej klasy, a dozwolone typy tras określa się, dodając obiekty 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();
    }
}

W przypadku większości aplikacji jedynym wymaganym typem trasy jest CATEGORY_REMOTE_PLAYBACK. Ten typ trasy traktuje urządzenie, na którym działa Twoja aplikacja, jako pilota. Podłączony odbiornik zajmuje się pobieraniem, dekodowaniem i odtwarzaniem wszystkich danych dotyczących treści. W ten sposób działają aplikacje obsługujące Google Cast, takie jak Chromecast.

Niektórzy producenci udostępniają specjalną opcję routingu o nazwie „dodatkowe urządzenie wyjściowe”. Dzięki temu ustawieniu routingu aplikacja do multimediów pobiera, renderuje i przesyła strumieniowo filmy oraz muzykę bezpośrednio na ekran lub głośniki wybranego odbiornika zdalnego. Za pomocą dodatkowego wyjścia możesz wysyłać treści do bezprzewodowych systemów muzycznych lub wyświetlaczy wideo. Aby umożliwić wykrywanie i wybieranie tych urządzeń, musisz do obiektu MediaRouteSelector dodać kategorię elementów sterujących CATEGORY_LIVE_AUDIO lub CATEGORY_LIVE_VIDEO. Musisz też utworzyć i obsługiwać własne okno Presentation.

Dodawanie przycisku kierowania multimediów do paska działań

Po zdefiniowaniu menu trasy multimediów i zdefiniowania opcji MediaRouteSelector możesz teraz dodać do aktywności przycisk trasy multimediów. Zastąp metodę onCreateOptionsMenu() w przypadku każdej aktywności, aby dodać menu opcji.

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;
}

Więcej informacji o implementowaniu paska działań w aplikacji znajdziesz w przewodniku dla programistów dotyczącym paska działań.

Możesz też dodać przycisk trasy multimediów jako MediaRouteButton w dowolnym widoku danych. Musisz dołączyć do przycisku obiekt MediaRouteSelector za pomocą metody setRouteSelector(). Zapoznaj się z listą kontrolną projektowania Google Cast, by dowiedzieć się, jak dodać przycisk trasy multimediów do swojej aplikacji.

Wywołania zwrotne MediaRouter

Wszystkie aplikacje uruchomione na tym samym urządzeniu mają 1 instancję MediaRouter i jej trasy (odfiltrowane według aplikacji MediaRouteSelector). Każde działanie komunikuje się z MediaRouter za pomocą własnej implementacji metod MediaRouter.Callback. MediaRouter wywołuje metody wywołania zwrotnego za każdym razem, gdy użytkownik wybiera, zmienia lub rozłącza trasę.

W wywołaniu zwrotnym jest kilka metod, które możesz zastąpić, aby otrzymywać informacje o zdarzeniach routingu. Implementacja klasy MediaRouter.Callback powinna zastąpić klasy onRouteSelected() i onRouteUnselected().

MediaRouter jest zasobem współdzielonym, więc Twoja aplikacja musi zarządzać wywołaniami zwrotnymi MediaRouter w odpowiedzi na zwykłe wywołania zwrotne cyklu życia aktywności:

  • Po utworzeniu aktywności (onCreate(Bundle)) chwyć wskaźnik do elementu MediaRouter i przytrzymaj go przez cały czas istnienia aplikacji.
  • Dołącz wywołania zwrotne do MediaRouter, gdy aktywność staje się widoczna (onStart()), i odłącz je, gdy jest ukryta (onStop()).

Poniższy przykładowy kod pokazuje, jak utworzyć i zapisać obiekt wywołania zwrotnego, jak uzyskać instancję MediaRouter i jak zarządzać wywołaniami zwrotnymi. Zwróć uwagę na użycie flagi CALLBACK_FLAG_REQUEST_DISCOVERY podczas dołączania wywołań zwrotnych w onStart(). Pozwoli to obiektowi MediaRouteSelector na odświeżenie listy dostępnych tras przycisku trasy multimediów.

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();
    }
    ...
}

Platforma routera multimediów udostępnia też klasę MediaRouteDiscoveryFragment, która ułatwia dodawanie i usuwanie wywołania zwrotnego dla działania.

Uwaga: jeśli tworzysz aplikację do odtwarzania muzyki i chcesz, aby aplikacja odtwarzała muzykę, gdy działa w tle, musisz utworzyć Service do odtwarzania i wywołać platformę routera multimediów za pomocą wywołań zwrotnych cyklu życia usługi.

Sterowanie trasą odtwarzania zdalnego

Gdy wybierzesz trasę zdalnego odtwarzania, aplikacja będzie pełnić funkcję pilota. Urządzenie po drugiej stronie trasy obsługuje wszystkie funkcje pobierania, dekodowania i odtwarzania danych o treści. Elementy sterujące w interfejsie aplikacji komunikują się z urządzeniem odbierającym za pomocą obiektu RemotePlaybackClient.

Klasa RemotePlaybackClient udostępnia dodatkowe metody zarządzania odtwarzaniem treści. Oto kilka najważniejszych metod odtwarzania z klasy RemotePlaybackClient:

  • play() – odtwarza określony plik multimedialny określony przez Uri.
  • pause() – wstrzymaj odtwarzaną aktualnie ścieżkę multimediów.
  • resume() – kontynuuj odtwarzanie bieżącego utworu po wykonaniu polecenia wstrzymania.
  • seek() – przejście do konkretnego miejsca w bieżącej ścieżce.
  • release() – przerwij połączenie aplikacji z urządzeniem zdalnego odtwarzania.

Za pomocą tych metod możesz dołączać działania do elementów sterujących odtwarzaniem, które są dostępne w Twojej aplikacji. Większość z nich pozwala też dołączać obiekt wywołania zwrotnego, aby umożliwić monitorowanie postępu zadania odtwarzania lub żądania kontrolnego.

Klasa RemotePlaybackClient obsługuje też dodawanie do kolejki wielu elementów multimedialnych na potrzeby odtwarzania i zarządzania kolejką multimediów.

Kod demonstracyjny

Przykłady korzystania z Android BasicMediaRouter i MediaRouter dodatkowo pokazują wykorzystanie interfejsu MediaRouter API.