如要完成用戶端/伺服器設計,您必須建構包含 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 應顯示媒體工作階段目前的狀態,如 PlaybackState 和 Metadata 所述。建立傳輸控制項時,您可以擷取工作階段目前的狀態並在 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
相關的可能仍可繼續運作,使用者無法查看或控制
播放。
應用程式
因此,當工作階段遭到刪除時,您必須中斷與
撥號方式: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 } };