סקירה כללית של MediaRouter

כדי להשתמש ב-MediaRouter framework באפליקציה, צריך לקבל מופע של האובייקט MediaRouter ומצרפים אובייקט MediaRouter.Callback להאזנה לאירועי ניתוב. תוכן שנשלח דרך מסלול מדיה עובר דרך המסלול משויך ל-MediaRouteProvider (חוץ מכמה מקרים מיוחדים, כגון מכשיר פלט Bluetooth). איור 1 מספק תמונה ברמה גבוהה המחלקות המשמשות לניתוב תוכן בין מכשירים.

איור 1. סקירה כללית של המחלקות העיקריות של נתבי מדיה באפליקציות.

הערה: אם רוצים שהאפליקציה תתמוך מכשירי Google Cast, יש להשתמש ב-Cast SDK ולבנות את האפליקציה כשולח Cast שלה. פועלים לפי ההוראות שמוצגות מסמכי תיעוד של הפעלת Cast במקום להשתמש ישירות במסגרת MediaRouter.

הלחצן של נתיב המדיה

באפליקציות ל-Android צריך להשתמש בלחצן של נתיב מדיה כדי לשלוט בניתוב מדיה. מסגרת MediaRouter מספק ממשק סטנדרטי ללחצן, שעוזר למשתמשים לזהות ניתוב ולהשתמש בו כשזה זמין. הלחצן של נתיב המדיה ממוקם בדרך כלל בצד ימין של סרגל הפעולות של האפליקציה, כפי שמוצג באיור 2.

איור 2. לחצן נתיב המדיה ב סרגל הפעולות.

כשהמשתמש לוחץ על הלחצן של נתיב המדיה, מסלולי המדיה הזמינים מופיעים ברשימה שמוצגת באיור 3.

איור 3. רשימה של מסלולי מדיה זמינים, שמוצגת לאחר לחיצה על הלחצן של נתיב המדיה.

יש לבצע את השלבים הבאים כדי ליצור לחצן למסלול מדיה:

  1. שימוש ב-AppCompatActivity
  2. הגדרת האפשרות בתפריט של הלחצן של נתיב המדיה
  3. יצירת MediaRouteSelector
  4. הוספת הלחצן של נתיב המדיה לסרגל הפעולות
  5. יצירה וניהול של methods של MediaRouter.Callback במחזור החיים של הפעילות

בקטע הזה מתוארים ארבעת השלבים הראשונים. הקטע הבא מתאר שיטות להתקשרות חזרה.

שימוש ב-AppCompatActivity

כשמשתמשים ב-framework של נתב המדיה בפעילות, צריך להרחיב את הפעילות מ-AppCompatActivity ולייבא את חבילה מס' androidx.appcompat.app. עליך להוסיף את androidx.appcompat:appcompat ו-androidx.mediarouter:mediarouter תמיכה בספריות של פרויקט פיתוח האפליקציה. למידע נוסף על הוספת ספריות תמיכה לפרויקט שלכם, ראו תחילת העבודה עם Android Jetpack.

זהירות: הקפידו להשתמש ב-androidx של ה-framework של נתב המדיה. אין להשתמש בחבילה הישנה של 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 וליצור את הבורר כשנוצרת הפעילות בקריאה אל MediaRouteSelector.Builder מהשיטה onCreate() , כמו שמוצג בדוגמת הקוד הבאה. שימו לב שהבורר נשמר במשתנה class, ומפורטים סוגי המסלולים המותרים. על ידי הוספת 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 קורא לשיטות הקריאה החוזרת בכל פעם שהמשתמש בוחר, משנה או מנתק מסלול.

יש מספר שיטות בקריאה החוזרת (callback) שאפשר לשנות כדי לקבל מידע עליהן אירועי ניתוב. לכל הפחות, ההטמעה של המחלקה MediaRouter.Callback צריכה לבטל onRouteSelected() והקבוצה onRouteUnselected().

MediaRouter הוא משאב משותף, ולכן האפליקציה שלך צריכה לנהל את הקריאות החוזרות של MediaRouter בתגובה לקריאה החוזרת (callback) הרגילה של מחזור החיים של הפעילות:

  • כשהפעילות נוצרת (onCreate(Bundle)), צריך לקחת את הסמן של העכבר מעל ה-MediaRouter ולהחזיק בו כל משך החיים של האפליקציה.
  • יש לצרף ל-MediaRouter קריאות חוזרות (callback) כשהפעילות תהיה גלויה (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();
    }
    ...
}

ה-framework של נתב המדיה מספק גם כיתה אחת (MediaRouteDiscoveryFragment), שאחראית להוספה ולהוספה הסרת הקריאה החוזרת לפעילות.

הערה: אם אתם כותבים אפליקציה להפעלת מוזיקה ורוצים שהיא תפעל בזמן שהיא פועלת ברקע, עליך ליצור Service להפעלה ולקרוא ל-framework של נתב המדיה מהקריאות החוזרות (callback) במחזור החיים של השירות.

שליטה במסלול ההפעלה מרחוק

כשבוחרים מסלול להפעלה מרחוק, האפליקציה משמשת כשלט רחוק. המכשיר בקצה השני של המסלול מטפל בכל הפונקציות לאחזור, פענוח וההפעלה של נתוני תוכן. הפקדים בממשק המשתמש של האפליקציה מתקשרים עם המכשיר המקבל באמצעות אובייקט RemotePlaybackClient.

בכיתה RemotePlaybackClient יש שיטות נוספות לניהול הפעלת תוכן. אלה כמה משיטות ההפעלה העיקריות בכיתה RemotePlaybackClient:

  • play() — הפעלת פריט ספציפי קובץ מדיה, שצוין באמצעות Uri.
  • pause() – משהים את טראק המדיה שמופעל עכשיו.
  • resume() – המשך השמעת הטראק הנוכחי לאחר פקודת השהיה.
  • seek() – העברה למאפיין ספציפי במיקום בטראק הנוכחי.
  • release() — יש להסיר את בחיבור בין האפליקציה למכשיר להפעלה מרחוק.

אפשר להשתמש בשיטות האלה כדי לצרף פעולות לפקדי ההפעלה שצוינו ב- אפליקציה. רוב השיטות האלה גם מאפשרות לכלול אובייקט של קריאה חוזרת (callback) כדי שתוכלו לעקוב את ההתקדמות של משימת ההפעלה או של בקשת הבקרה.

המחלקה RemotePlaybackClient תומכת גם בתור מספר פריטי מדיה להפעלה ולניהול של תור המדיה.

קוד לדוגמה

ה-Android BasicMediaRouter ו-MediaRouter דוגמאות נוספות מדגימות את השימוש ב-MediaRouter API.