如要完成用戶端/伺服器設計,您必須建立包含 UI 程式碼、相關聯的 MediaController 和 MediaBrowser 的活動元件。
MediaBrowser 會執行兩項重要功能:它會連線至 MediaBrowserService,連線時即會為您的使用者介面建立 MediaController。
注意事項: 建議採用的 MediaBrowser 實作方式為 MediaBrowserCompat
,詳情請參閱 Media-Compat 支援資料庫。在這個頁面中,「MediaBrowser」是指 MediaBrowserCompat 的執行個體,
連線至 MediaBrowserService
用戶端活動建立完成後,就會連線至 MediaBrowserService。握手與舞蹈息息相關。 請修改活動的生命週期回呼,如下所示:
onCreate()
會建構 MediaBrowserCompat。傳入 MediaBrowserService 和您定義的 MediaBrowserCompat.ConnectionCallback 名稱。onStart()
會連線至 MediaBrowserService。這就派上用場了 MediaBrowserCompat.ConnectionCallback 的強大魔力。如果連線成功,onConnect() 回呼會建立媒體控制器、將其連結至媒體工作階段、將 UI 控制項連結至 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 } };
將 UI 連結至媒體控制器
在上方的 ConnectionCallback 程式碼範例中,加入對 buildTransportControls()
的呼叫來縮小 UI。您必須為控製播放器的 UI 元素設定 onClickListener。請為每個選項選擇適當的 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
方法。
與媒體工作階段保持同步
使用者介面應顯示媒體工作階段的目前狀態,如播放狀態和中繼資料所述。建立傳輸控制項時,您可以擷取工作階段的目前狀態、在 UI 中顯示,並根據狀態和可用動作啟用及停用傳輸控制項。
如要在每次狀態或中繼資料變更時接收來自媒體工作階段的回呼,請使用以下兩種方法定義 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
相關的函式可能可以繼續運作,但使用者無法從遭刪除的媒體工作階段查看或控製播放功能,因而可能降低應用程式的價值。
因此,刪除工作階段時,您必須呼叫 disconnect()
來中斷與 MediaBrowserService
的連線。這樣做可確保瀏覽器服務沒有繫結的用戶端,且可由 OS 刪除。如果您需要稍後重新連線至 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 } };