Membuat klien browser media

Untuk melengkapi desain klien/server, Anda harus membuat komponen aktivitas yang berisi kode UI, MediaController yang terkait, dan MediaBrowser.

MediaBrowser melakukan dua fungsi penting: Membuat sambungan ke MediaBrowserService dan, setelah tersambung, membuat MediaController untuk UI Anda.

Catatan: Implementasi MediaBrowser yang direkomendasikan adalah MediaBrowserCompat, yang ditentukan dalam support library Media-Compat. Di sepanjang halaman ini, istilah "MediaBrowser" mengacu pada instance MediaBrowserCompat.

Membuat sambungan ke MediaBrowserService

Saat aktivitas klien Anda dibuat, aktivitas tersebut akan tersambung ke MediaBrowserService. Sedikit penyesuaian perlu dilakukan di sini. Ubah callback siklus proses aktivitas sebagai berikut:

  • onCreate() menyusun MediaBrowserCompat. Masukkan nama MediaBrowserService dan MediaBrowserCompat.ConnectionCallback yang telah ditetapkan.
  • onStart() terhubung ke MediaBrowserService. Di sinilah kecanggihan MediaBrowserCompat.ConnectionCallback berperan. Jika sambungan berhasil, callback onConnect() akan membuat pengontrol media, menautkannya ke sesi media, menautkan kontrol UI Anda ke MediaController, dan mendaftarkan pengontrol untuk menerima callback dari sesi media.
  • onResume() menetapkan aliran audio sehingga aplikasi Anda merespons kontrol volume di perangkat.
  • onStop() memutus sambungan MediaBrowser dan membatalkan pendaftaran MediaController.Callback saat aktivitas Anda berhenti.

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();

  }
}

Menyesuaikan MediaBrowserCompat.ConnectionCallback

Saat aktivitas Anda menyusun MediaBrowserCompat, Anda harus membuat instance ConnectionCallback. Modifikasi metode onConnected()-nya untuk mengambil token sesi media dari MediaBrowserService, lalu gunakan token tersebut untuk membuat MediaControllerCompat.

Gunakan metode praktis MediaControllerCompat.setMediaController() untuk menyimpan link ke pengontrol. Hal ini memungkinkan penanganan tombol media. Hal ini juga memungkinkan Anda memanggil MediaControllerCompat.getMediaController() untuk mengambil pengontrol saat mem-build kontrol transport.

Contoh kode berikut menunjukkan cara memodifikasi metode 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
    }
  };

Menyambungkan UI Anda ke pengontrol media

Dalam contoh kode ConnectionCallback di atas, sertakan panggilan ke buildTransportControls() untuk menyempurnakan UI Anda. Anda harus menetapkan onClickListeners untuk elemen UI yang mengontrol pemutar. Pilih metode MediaControllerCompat.TransportControls yang sesuai untuk setiap metode.

Kode Anda akan terlihat seperti ini, dengan onClickListener untuk setiap tombol:

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

Metode TransportControls mengirim callback ke sesi media layanan Anda. Pastikan Anda telah menentukan metode MediaSessionCompat.Callback yang sesuai untuk setiap kontrol.

Mempertahankan sinkronisasi dengan sesi media

UI harus menampilkan status sesi media saat ini, seperti yang dijelaskan oleh PlaybackState dan Metadata-nya. Saat membuat kontrol transport, Anda dapat mengambil status sesi saat ini, menampilkannya di UI aplikasi Anda, serta mengaktifkan dan menonaktifkan kontrol transport berdasarkan status dan tindakan yang tersedia.

Untuk menerima callback dari sesi media setiap kali status atau metadatanya berubah, tentukan MediaControllerCompat.Callback, dengan dua metode berikut:

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

Daftarkan callback ini ketika Anda membuat kontrol transport (lihat metode buildTransportControls()) dan batalkan pendaftarannya saat aktivitas berhenti (dalam metode siklus proses onStop() aktivitas.

Putuskan koneksi saat sesi media dihancurkan

Jika sesi media menjadi tidak valid, callback onSessionDestroyed() akan dikeluarkan. Jika hal itu terjadi, sesi tidak dapat berfungsi lagi selama masa aktif MediaBrowserService. Meskipun fungsi yang terkait dengan MediaBrowser dapat terus berfungsi, pengguna tidak dapat melihat atau mengontrol pemutaran dari sesi media yang telah dihancurkan, yang kemungkinan akan mengurangi nilai aplikasi Anda.

Oleh karena itu, saat sesi dihancurkan, Anda harus memutuskan sambungan dari MediaBrowserService dengan memanggil disconnect(). Hal ini memastikan bahwa layanan browser tidak memiliki klien yang terikat dan dapat dihancurkan oleh OS. Jika nanti Anda harus menghubungkan kembali ke MediaBrowserService (misalnya, jika aplikasi Anda ingin mempertahankan koneksi persisten ke aplikasi media), buat instance MediaBrowser baru, bukan menggunakan kembali instance lama.

Cuplikan kode berikut menunjukkan implementasi callback yang terputus dari layanan browser ketika sesi media dihancurkan:

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