פיתוח לקוח של דפדפן מדיה

כדי להשלים את התכנון של הלקוח/שרת, יש ליצור רכיב פעילות שמכיל את קוד ממשק המשתמש שלכם, MediaController המשויך ו-MediaBrowser.

ה-MediaBrowser מבצע שתי פונקציות חשובות: הוא מתחבר אל MediaBrowserService ולאחר החיבור שלו יוצר את MediaController עבור ממשק המשתמש שלכם.

הערה: ההטמעה המומלצת של MediaBrowser היא MediaBrowserCompat, שמוגדר ספריית התמיכה לתאימות מדיה. בכל הדף הזה מופיע המונח "MediaBrowser" מתייחס למופע של MediaBrowserCompat.

התחברות אל MediaBrowserService

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

  • onCreate() בונה MediaBrowserCompat. מעבירים את השם של MediaBrowserService ושל MediaBrowserCompat.ConnectionCallback שהגדרתם.
  • onStart() מתחבר אל MediaBrowserService. כאן נכנס לתוקף הקסם של MediaBrowserCompat.ConnectionCallback. אם החיבור מתבצע בהצלחה, הקריאה החוזרת של onConnect() יוצרת את בקר המדיה, מקשרת אותו להפעלת המדיה, מקשרת את פקדי ממשק המשתמש ל-MediaController, ורושמת את הבקר כדי לקבל קריאות חוזרות מסשן המדיה.
  • 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);
}
}

ה-methods של 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) {}
  };

רושמים את הקריאה החוזרת (callback) כשמפתחים את אמצעי בקרת התעבורה (ראו השיטה buildTransportControls()) ומבטלים את הרישום כשהפעילות נפסקת (בשיטת מחזור החיים onStop() של הפעילות).

התנתקות כשסשן המדיה מושמד

אם סשן המדיה הופך ללא תקין, onSessionDestroyed() מתבצעת קריאה חוזרת. כשזה קורה, הסשן לא יכול לפעול. שוב במהלך משך החיים של MediaBrowserService. למרות שהפונקציות שקשורים אל MediaBrowser עשויים להמשיך לפעול, משתמשים לא יכולים לצפות או לשלוט מסשן מדיה שהושמד, דבר שעלול לגרום לירידה בערך של את האפליקציה שלך.

לכן, כשהסשן מושמד, חובה להתנתק MediaBrowserService בשיחה disconnect() הדבר מבטיח שלשירות הדפדפן אין לקוחות קשורים ניתן להרוס אותו מערכת ההפעלה. אם צריך להתחבר מחדש אל MediaBrowserService מאוחר יותר (לדוגמה, אם האפליקציה רוצה לשמור על חיבור יציב לאפליקציית המדיה), ליצור מופע חדש של MediaBrowser במקום להשתמש שוב במופע הישן.

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

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