如要完成用戶端/伺服器設計,您必須建構包含 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。
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()
方法。
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
}
};
將 UI 連線至媒體控制器
在上方的 ConnectionCallback 程式碼範例中,加入對 buildTransportControls()
的呼叫,以完整顯示 UI。您需要為控製播放器的 UI 元素設定 onClickListener。選擇適當的設定
每種方法的 MediaControllerCompat.TransportControls
方法。
您的程式碼看起來會像這樣,每個按鈕都有一個 onClickListener:
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);
}
}
TransportControls 方法會將回呼傳送至服務的媒體工作階段。請務必先定義相應的
每個控制項的 MediaSessionCompat.Callback
方法。
與媒體工作階段保持同步
UI 應顯示媒體工作階段目前的狀態,如 PlaybackState 和 Metadata 所述。建立傳輸控制項時,您可以擷取工作階段目前的狀態並在 UI 中顯示該工作階段,並根據狀態和可用的動作啟用和停用傳輸控制項。
如要在媒體工作階段每次狀態或中繼資料變更時接收回呼,請定義
MediaControllerCompat.Callback
,使用以下兩種方法:
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) {}
};
在建立傳輸控制項時註冊回呼 (請參閱 buildTransportControls()
方法),並在活動停止時取消註冊 (在活動的 onStop()
生命週期方法中)。
在媒體工作階段刪除時中斷連線
如果媒體工作階段失效,
onSessionDestroyed()
敬上
回呼。在這種情況下,工作階段就無法正常運作
必須在 MediaBrowserService
生命週期內再次執行。雖然函式
與 MediaBrowser
相關的可能仍可繼續運作,使用者無法查看或控制
播放。
應用程式
因此,當工作階段遭到刪除時,您必須中斷與
撥號方式:MediaBrowserService
disconnect()
。
這可確保瀏覽器服務沒有繫結的用戶端,且
可能會遭到
作業系統。
如果您之後需要重新連線至 MediaBrowserService
(例如
應用程式想要持續與媒體應用程式連線),
建立 MediaBrowser
的新例項,而不是重複使用舊的例項。
下列程式碼片段示範的回呼實作會 當媒體工作階段刪除時,瀏覽器服務也會中斷連線:
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
}
};