نمای کلی MediaRouter

برای استفاده از چارچوب MediaRouter در برنامه‌تان، باید نمونه‌ای از شیء MediaRouter را دریافت کنید و شیء MediaRouter.Callback را برای گوش دادن به رویدادهای مسیریابی پیوست کنید. محتوای ارسال‌شده ازطریق مسیر رسانه از MediaRouteProvider مرتبط با مسیر عبور می‌کند (به‌جز در چند مورد خاص، مانند دستگاه خروجی بلوتوث). شکل ۱ نمای سطح بالایی از کلاس‌های مورداستفاده برای مسیریابی محتوا بین دستگاه‌ها ارائه می‌دهد.

شکل ۱. نمای کلی کلاس‌های اصلی مسیریاب رسانه که برنامه‌ها از آن‌ها استفاده می‌کنند.

توجه: اگر می‌خواهید برنامه‌تان از دستگاه‌های Google Cast پشتیبانی کند، باید از Cast SDK استفاده کنید و برنامه‌تان را به‌عنوان فرستنده Cast بسازید. به‌جای استفاده مستقیم از چارچوب MediaRouter، دستورالعمل‌های موجود در اسناد Cast را دنبال کنید.

دکمه مسیر رسانه

برنامه‌های Android باید از دکمه مسیر رسانه برای کنترل کردن مسیریابی رسانه استفاده کنند. چارچوب MediaRouter میانای استانداردی برای دکمه ارائه می‌دهد که به کاربران کمک می‌کند مسیریابی را وقتی دردسترس است تشخیص دهند و از آن استفاده کنند. دکمه مسیر رسانه معمولاً در سمت راست نوار اقدام برنامه قرار می‌گیرد، همان‌طور که در شکل ۲ نشان داده شده است.

شکل ۲. دکمه مسیر رسانه در نوار اقدام.

وقتی کاربر دکمه مسیر رسانه را فشار می‌دهد، مسیرهای رسانه دردسترس در فهرستی نشان داده می‌شود، همان‌طور که در شکل ۳ نشان داده شده است.

شکل ۳. فهرستی از مسیرهای رسانه دردسترس که پس‌از فشار دادن دکمه مسیر رسانه نشان داده می‌شود.

برای ایجاد دکمه مسیر رسانه، این مراحل را دنبال کنید:

  1. استفاده از AppCompatActivity
  2. تعریف گزینه منو دکمه مسیر رسانه
  3. ایجاد MediaRouteSelector
  4. دکمه مسیر رسانه را به نوار اقدام اضافه کنید
  5. روش‌های MediaRouter.Callback را در چرخه حیات فعالیتتان ایجاد و مدیریت کنید

این بخش چهار مرحله اول را شرح می‌دهد. بخش بعدی روش‌های «فراخوانی» را شرح می‌دهد.

استفاده از AppCompatActivity

وقتی از چارچوب مسیریاب رسانه در فعالیتی استفاده می‌کنید، باید فعالیت را از AppCompatActivity گسترش دهید و بسته 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 گسترش دهید و وقتی فعالیت ایجاد شد، انتخابگر را بسازید و MediaRouteSelector.Builder را از روش onCreate()‎ فراخوانی کنید، همان‌طور که در نمونه کد زیر نشان داده شده است. توجه داشته باشید که گزینشگر در متغیر کلاس ذخیره می‌شود و انواع مسیرهای مجاز با افزودن اشیاء MediaControlIntent مشخص می‌شوند:

کاتلین

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

جاوا

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() را برای هریک از فعالیت‌هایتان لغو کنید تا منوِ گزینه‌ها اضافه شود.

کاتلین

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
}

جاوا

@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 آن برنامه فیلتر می‌شود). هر فعالیت بااستفاده از پیاده‌سازی خودش از روش‌های MediaRouter.Callback با MediaRouter ارتباط برقرار می‌کند. MediaRouter هر زمان که کاربر مسیری را انتخاب، تغییر، یا قطع کند، روش‌های فراخوانی را فراخوانی می‌کند.

چند روش در فراخوانی برگشتی وجود دارد که می‌توانید برای دریافت اطلاعات درباره رویدادهای مسیریابی لغو کنید. حداقل، پیاده‌سازی شما از کلاس MediaRouter.Callback باید onRouteSelected() و onRouteUnselected() را لغو کند.

ازآنجایی‌که MediaRouter یک منبع مشترک است، برنامه شما باید فراخوانی‌های MediaRouter خود را در پاسخ به فراخوانی‌های معمول چرخه حیات فعالیت مدیریت کند:

  • وقتی فعالیت ایجاد شد (onCreate(Bundle))، اشاره‌گری به MediaRouter بگیرید و آن را برای طول عمر برنامه نگه دارید.
  • وقتی فعالیت نمایان می‌شود (onStart())، فراخوانی‌های برگشتی را به MediaRouter متصل کنید و وقتی پنهان می‌شود (onStop())، آن‌ها را جدا کنید.

نمونه کد زیر نحوه ایجاد و ذخیره کردن شیء callback، نحوه دریافت نمونه‌ای از MediaRouter، و نحوه مدیریت کردن callback را نشان می‌دهد. هنگام پیوست کردن فراخوان‌های برگشتی در onStart()، به استفاده از پرچم CALLBACK_FLAG_REQUEST_DISCOVERY توجه کنید. این کار به MediaRouteSelector شما اجازه می‌دهد تا فهرست مسیرهای دردسترس دکمه مسیر رسانه را بازآوری کند.

کاتلین

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

جاوا

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

چارچوب مسیریاب رسانه همچنین یک کلاس MediaRouteDiscoveryFragment ارائه می‌دهد که وظیفه افزودن و حذف کردن فراخوان برگشتی برای یک فعالیت را برعهده دارد.

توجه: اگر درحال نوشتن برنامه بازپخش موسیقی هستید و می‌خواهید برنامه موسیقی را در پس‌زمینه پخش کند، باید Service برای بازپخش بسازید و چارچوب مسیریاب رسانه را از فراخوانی‌های چرخه حیات «سرویس» فراخوانی کنید.

کنترل کردن مسیر بازپخش از راه دور

وقتی مسیر بازپخش ازراه‌دور را انتخاب می‌کنید، برنامه‌تان به‌عنوان کنترل ازراه‌دور عمل می‌کند. دستگاه موجود در انتهای دیگر مسیر همه عملکردهای بازیابی، رمزگشایی، و بازپخش داده‌های محتوا را مدیریت می‌کند. کنترل‌های موجود در واسط کاربر برنامه‌تان بااستفاده از شیء RemotePlaybackClient با دستگاه گیرنده ارتباط برقرار می‌کنند.

کلاس RemotePlaybackClient روش‌های بیشتری برای مدیریت بازپخش محتوا ارائه می‌دهد. در اینجا چند روش کلیدی بازپخش از کلاس RemotePlaybackClient آورده شده است:

  • play() — پخش فایل رسانه‌ای خاصی که با Uri مشخص شده است.
  • pause() — قطعه رسانه‌ای را که درحال پخش است موقتاً متوقف می‌کند.
  • resume() — ادامه پخش قطعه فعلی پس‌از فرمان توقف موقت.
  • seek() — رفتن به موقعیت خاصی در قطعه فعلی.
  • release() — اتصال بین برنامه و دستگاه بازپخش ازراه‌دور را قطع کنید.

می‌توانید از این روش‌ها برای پیوست کردن کنش‌ها به کنترل‌های بازپخش که در برنامه‌تان ارائه می‌دهید استفاده کنید. اکثر این روش‌ها همچنین به شما امکان می‌دهند که یک شیء فراخوانی برگشتی را اضافه کنید تا بتوانید پیشرفت درخواست کنترل یا وظیفه بازپخش را نظارت کنید.

کلاس RemotePlaybackClient همچنین از صف‌بندی چندین فایل رسانه‌ای برای بازپخش و مدیریت صف رسانه پشتیبانی می‌کند.

کدگذاری نمونه

نمونه‌های Android BasicMediaRouter و MediaRouter استفاده از MediaRouter API را بیشتر نشان می‌دهند.