إنشاء برنامج متصفح للوسائط

لإكمال تصميم العميل/الخادم، يجب عليك إنشاء مكون نشاط يحتوي على رمز واجهة المستخدم، ووحدة تحكم وسائط مرتبطة، وMediaBrowser.

يؤدي MediaBrowser وظيفتين مهمتين: يتصل بـ MediaBrowserService، وعند الربط ينشئ MediaController لواجهة المستخدم.

ملاحظة: التنفيذ المُقترَح لأداة MediaBrowser هو MediaBrowserCompat، وهو محدّد في مكتبة دعم Media-Compat. في هذه الصفحة، يشير المصطلح "MediaBrowser" إلى مثيل من MediaBrowserCompat.

الربط بخدمة MediaBrowserService

عند إنشاء نشاط العميل، يتم الاتصال بـ MediaBrowserService. ويشمل ذلك القليل من المصافحة والرقص. عدِّل عمليات معاودة الاتصال في مراحل نشاط النشاط على النحو التالي:

  • تنشئ onCreate() واجهة MediaBrowserCompat. مرِّر باسم MediaBrowserService وMediaBrowserCompat.ConnectionCallback التي حددتها.
  • يتصل onStart() بـ MediaBrowserService. هنا يأتي دور سحر MediaBrowserCompat.ConnectionCallback. إذا كان الاتصال ناجحًا، فإن استدعاء onConnect() ينشئ وحدة التحكم في الوسائط، ويربطها بجلسة تشغيل الوسائط، ويربط عناصر التحكم في واجهة المستخدم بوحدة التحكم بالوسائط، ويسجل وحدة التحكم لتلقي استدعاءات من جلسة تشغيل الوسائط.
  • يضبط onResume() البث الصوتي حتى يستجيب تطبيقك للتحكّم في مستوى الصوت على الجهاز.
  • يؤدي onStop() إلى إلغاء ربط MediaBrowser وإلغاء تسجيل MediaController.Callback عند توقف نشاطك.

Kotlin

class MediaPlayerActivity : AppCompatActivity() {

    private lateinit var mediaBrowser: MediaBrowserCompat

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        // Create MediaBrowserServiceCompat
        mediaBrowser = MediaBrowserCompat(
                this,
                ComponentName(this, MediaPlaybackService::class.java),
                connectionCallbacks,
                null // optional Bundle
        )
    }

    public override fun onStart() {
        super.onStart()
        mediaBrowser.connect()
    }

    public override fun onResume() {
        super.onResume()
        volumeControlStream = AudioManager.STREAM_MUSIC
    }

    public override fun onStop() {
        super.onStop()
        // (see "stay in sync with the MediaSession")
        MediaControllerCompat.getMediaController(this)?.unregisterCallback(controllerCallback)
        mediaBrowser.disconnect()
    }
}

Java

public class MediaPlayerActivity extends AppCompatActivity {
  private MediaBrowserCompat mediaBrowser;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    // Create MediaBrowserServiceCompat
    mediaBrowser = new MediaBrowserCompat(this,
      new ComponentName(this, MediaPlaybackService.class),
        connectionCallbacks,
        null); // optional Bundle
  }

  @Override
  public void onStart() {
    super.onStart();
    mediaBrowser.connect();
  }

  @Override
  public void onResume() {
    super.onResume();
    setVolumeControlStream(AudioManager.STREAM_MUSIC);
  }

  @Override
  public void onStop() {
    super.onStop();
    // (see "stay in sync with the MediaSession")
    if (MediaControllerCompat.getMediaController(MediaPlayerActivity.this) != null) {
      MediaControllerCompat.getMediaController(MediaPlayerActivity.this).unregisterCallback(controllerCallback);
    }
    mediaBrowser.disconnect();

  }
}

تخصيص MediaBrowserCompat.ConnectionCallback

عندما ينشئ نشاطك MediaBrowserCompat، يجب إنشاء مثيل لـ ConnectionCallback. عدِّل طريقة onConnected() لاسترداد الرمز المميّز لجلسة الوسائط من MediaBrowserService، واستخدِم الرمز المميّز لإنشاء MediaControllerCompat.

استخدِم الطريقة الملائمة MediaControllerCompat.setMediaController() لحفظ رابط إلى وحدة التحكّم. يؤدي ذلك إلى تفعيل التعامل مع أزرار الوسائط. ويسمح لك أيضًا باستدعاء MediaControllerCompat.getMediaController() لاسترداد وحدة التحكّم عند إنشاء عناصر التحكّم في النقل.

يعرض نموذج الرمز التالي كيفية تعديل طريقة onConnected().

Kotlin

private val connectionCallbacks = object : MediaBrowserCompat.ConnectionCallback() {
    override fun onConnected() {

        // Get the token for the MediaSession
        mediaBrowser.sessionToken.also { token ->

            // Create a MediaControllerCompat
            val mediaController = MediaControllerCompat(
                    this@MediaPlayerActivity, // Context
                    token
            )

            // Save the controller
            MediaControllerCompat.setMediaController(this@MediaPlayerActivity, mediaController)
        }

        // Finish building the UI
        buildTransportControls()
    }

    override fun onConnectionSuspended() {
        // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    override fun onConnectionFailed() {
        // The Service has refused our connection
    }
}

Java

private final MediaBrowserCompat.ConnectionCallback connectionCallbacks =
  new MediaBrowserCompat.ConnectionCallback() {
    @Override
    public void onConnected() {

      // Get the token for the MediaSession
      MediaSessionCompat.Token token = mediaBrowser.getSessionToken();

      // Create a MediaControllerCompat
      MediaControllerCompat mediaController =
        new MediaControllerCompat(MediaPlayerActivity.this, // Context
        token);

      // Save the controller
      MediaControllerCompat.setMediaController(MediaPlayerActivity.this, mediaController);

      // Finish building the UI
      buildTransportControls();
    }

    @Override
    public void onConnectionSuspended() {
      // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    @Override
    public void onConnectionFailed() {
      // The Service has refused our connection
    }
  };

توصيل واجهة المستخدم بوحدة التحكّم في الوسائط

في نموذج رمز ConnectionCallback أعلاه، يتضمن استدعاءً إلى buildTransportControls() لتوضيح واجهة المستخدم. ستحتاج إلى ضبط onClickListeners لعناصر واجهة المستخدم التي تتحكم في المشغّل. اختَر طريقة MediaControllerCompat.TransportControls المناسبة لكل طريقة.

ستبدو التعليمة البرمجية على النحو التالي، مع onClickListener لكل زر:

Kotlin

fun buildTransportControls() {
    val mediaController = MediaControllerCompat.getMediaController(this@MediaPlayerActivity)
    // Grab the view for the play/pause button
    playPause = findViewById<ImageView>(R.id.play_pause).apply {
        setOnClickListener {
            // Since this is a play/pause button, you'll need to test the current state
            // and choose the action accordingly

            val pbState = mediaController.playbackState.state
            if (pbState == PlaybackStateCompat.STATE_PLAYING) {
                mediaController.transportControls.pause()
            } else {
                mediaController.transportControls.play()
            }
        }
    }

    // Display the initial state
    val metadata = mediaController.metadata
    val pbState = mediaController.playbackState

    // Register a Callback to stay in sync
    mediaController.registerCallback(controllerCallback)
}

Java

void buildTransportControls()
{
  // Grab the view for the play/pause button
  playPause = (ImageView) findViewById(R.id.play_pause);

  // Attach a listener to the button
  playPause.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // Since this is a play/pause button, you'll need to test the current state
      // and choose the action accordingly

      int pbState = MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getPlaybackState().getState();
      if (pbState == PlaybackStateCompat.STATE_PLAYING) {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().pause();
      } else {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().play();
      }
  });

  MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MediaPlayerActivity.this);

  // Display the initial state
  MediaMetadataCompat metadata = mediaController.getMetadata();
  PlaybackStateCompat pbState = mediaController.getPlaybackState();

  // Register a Callback to stay in sync
  mediaController.registerCallback(controllerCallback);
}
}

ترسل طرق TransportControls استدعاءات إلى جلسة وسائط الخدمة. تأكَّد من تحديد طريقة MediaSessionCompat.Callback مقابلة لكل عنصر تحكّم.

الحفاظ على المزامنة مع جلسة تشغيل الوسائط

وينبغي أن تعرض واجهة المستخدم الحالة الحالية لجلسة تشغيل الوسائط، كما هو موضّح في "حالة التشغيل" و"البيانات الوصفية". عند إنشاء عناصر التحكم في النقل، يمكنك الحصول على الحالة الحالية للجلسة وعرضها في واجهة المستخدم وتفعيل عناصر التحكم في النقل أو إيقافها بناءً على الحالة وإجراءاتها المتاحة.

لتلقّي طلبات معاودة الاتصال من جلسة تشغيل الوسائط في كل مرة تتغير فيها حالتها أو بياناتها الوصفية، حدِّد السمة MediaControllerCompat.Callback باستخدام الطريقتين التاليتين:

Kotlin

private var controllerCallback = object : MediaControllerCompat.Callback() {

    override fun onMetadataChanged(metadata: MediaMetadataCompat?) {}

    override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {}
}

Java

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onMetadataChanged(MediaMetadataCompat metadata) {}

    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {}
  };

يمكنك تسجيل طلب معاودة الاتصال عند إنشاء عناصر التحكّم في النقل (راجِع طريقة buildTransportControls()) وإلغاء تسجيله عند توقّف النشاط (ضمن طريقة دورة حياة النشاط onStop()).

قطع الاتصال عند تدمير جلسة الوسائط

وإذا أصبحت جلسة الوسائط غير صالحة، سيتم إصدار معاودة الاتصال بـ onSessionDestroyed(). وعند حدوث ذلك، لا يمكن أن تعمل الجلسة مرة أخرى خلال فترة بقاء MediaBrowserService. على الرغم من أنّ الدوال المتعلّقة بـ MediaBrowser قد تستمر في العمل، لا يمكن للمستخدم الاطّلاع على عملية التشغيل من جلسة وسائط تالفة أو التحكّم فيها، ما قد يؤدي على الأرجح إلى تقليل قيمة التطبيق.

لذلك، عند إيقاف الجلسة، يجب قطع الاتصال بخدمة MediaBrowserService من خلال طلب الرمز disconnect(). ويضمن ذلك ألّا يكون لخدمة المتصفّح أي عملاء محدّدين ويمكن تدميرها من خلال نظام التشغيل. إذا كنت بحاجة إلى إعادة الاتصال بـ MediaBrowserService لاحقًا (على سبيل المثال، إذا كان تطبيقك يريد الحفاظ على اتصال دائم بتطبيق الوسائط)، عليك إنشاء مثيل جديد من MediaBrowser بدلاً من إعادة استخدام القديم القديم.

يوضح مقتطف الرمز التالي تنفيذ معاودة الاتصال الذي يتم فصله عن خدمة المتصفح عند تدمير جلسة الوسائط:

Kotlin

private var controllerCallback = object : MediaControllerCompat.Callback() {
    override fun onSessionDestroyed() {
      mediaBrowser.disconnect()
      // maybe schedule a reconnection using a new MediaBrowser instance
    }
}

Java

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onSessionDestroyed() {
      mediaBrowser.disconnect();
      // maybe schedule a reconnection using a new MediaBrowser instance
    }
  };