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

כדי להשלים את התכנון של הלקוח/שרת, יש ליצור רכיב פעילות שמכיל את קוד ממשק המשתמש שלכם, 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 כשהפעילות מופסקת.
KotlinJava
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()
   
}
}
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().

KotlinJava
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
   
}
}
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 לכל לחצן:

KotlinJava
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)
}
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, בשתי השיטות האלה:

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

   
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {}

   
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {}
}
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) מתנתק משירות הדפדפן כאשר פעילות המדיה נמחקת:

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