การสร้างบริการเบราว์เซอร์สื่อ

แอปของคุณต้องประกาศ MediaBrowserService ด้วยตัวกรอง Intent ในไฟล์ Manifest คุณเลือกชื่อบริการของตนเองได้ ในตัวอย่างต่อไปนี้คือ "MediaPlaybackService"

<service android:name=".MediaPlaybackService">
  <intent-filter>
    <action android:name="android.media.browse.MediaBrowserService" />
  </intent-filter>
</service>

หมายเหตุ: การใช้งานที่แนะนำของ MediaBrowserService มีค่า MediaBrowserServiceCompat ซึ่งระบุไว้ใน media-compat support library ในหน้านี้ คำว่า "MediaBrowserService" หมายถึงอินสแตนซ์ จาก MediaBrowserServiceCompat

เริ่มต้นเซสชันสื่อ

เมื่อบริการได้รับเมธอด Callback สำหรับวงจรของ onCreate() บริการควรทําตามขั้นตอนต่อไปนี้

โค้ด onCreate() ด้านล่างแสดงขั้นตอนเหล่านี้

Kotlin

private const val MY_MEDIA_ROOT_ID = "media_root_id"
private const val MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id"

class MediaPlaybackService : MediaBrowserServiceCompat() {

    private var mediaSession: MediaSessionCompat? = null
    private lateinit var stateBuilder: PlaybackStateCompat.Builder

    override fun onCreate() {
        super.onCreate()

        // Create a MediaSessionCompat
        mediaSession = MediaSessionCompat(baseContext, LOG_TAG).apply {

            // Enable callbacks from MediaButtons and TransportControls
            setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
                    or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
            )

            // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
            stateBuilder = PlaybackStateCompat.Builder()
                    .setActions(PlaybackStateCompat.ACTION_PLAY
                                    or PlaybackStateCompat.ACTION_PLAY_PAUSE
                    )
            setPlaybackState(stateBuilder.build())

            // MySessionCallback() has methods that handle callbacks from a media controller
            setCallback(MySessionCallback())

            // Set the session's token so that client activities can communicate with it.
            setSessionToken(sessionToken)
        }
    }
}

Java

public class MediaPlaybackService extends MediaBrowserServiceCompat {
    private static final String MY_MEDIA_ROOT_ID = "media_root_id";
    private static final String MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id";

    private MediaSessionCompat mediaSession;
    private PlaybackStateCompat.Builder stateBuilder;

    @Override
    public void onCreate() {
        super.onCreate();

        // Create a MediaSessionCompat
        mediaSession = new MediaSessionCompat(context, LOG_TAG);

        // Enable callbacks from MediaButtons and TransportControls
        mediaSession.setFlags(
              MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
              MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

        // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
        stateBuilder = new PlaybackStateCompat.Builder()
                            .setActions(
                                PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PLAY_PAUSE);
        mediaSession.setPlaybackState(stateBuilder.build());

        // MySessionCallback() has methods that handle callbacks from a media controller
        mediaSession.setCallback(new MySessionCallback());

        // Set the session's token so that client activities can communicate with it.
        setSessionToken(mediaSession.getSessionToken());
    }
}

จัดการการเชื่อมต่อไคลเอ็นต์

MediaBrowserService มี 2 วิธีที่ใช้จัดการกับการเชื่อมต่อไคลเอ็นต์ ได้แก่ ตัวควบคุม onGetRoot() รายการ การเข้าถึงบริการ และ onLoadChildren() ทำให้ลูกค้าสามารถสร้างและแสดงเมนูในลำดับชั้นเนื้อหาของ MediaBrowserService

การควบคุมการเชื่อมต่อไคลเอ็นต์ด้วย onGetRoot()

เมธอด onGetRoot() จะแสดงโหนดรากของลำดับชั้นเนื้อหา หาก เมธอดแสดงผลเป็น Null เนื่องจากการเชื่อมต่อถูกปฏิเสธ

หากต้องการให้ลูกค้าเชื่อมต่อกับบริการของคุณและเรียกดูเนื้อหาสื่อของบริการได้ onGetRoot() ต้องแสดงผล BrowserRoot ที่ไม่เป็นค่าว่างซึ่งเป็นรหัสรากที่ แสดงถึงลำดับชั้นเนื้อหา

หากต้องการให้ไคลเอ็นต์เชื่อมต่อกับ MediaSession ของคุณโดยไม่ต้องเรียกดู onGetRoot() ยังคงต้องแสดงผล BrowserRoot ที่ไม่เป็นค่าว่าง แต่รหัสรูทควรแสดง ลำดับชั้นเนื้อหาที่ว่างเปล่า

การใช้งาน onGetRoot() โดยทั่วไปอาจมีลักษณะดังนี้

Kotlin

override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
): MediaBrowserServiceCompat.BrowserRoot {

    // (Optional) Control the level of access for the specified package name.
    // You'll need to write your own logic to do this.
    return if (allowBrowsing(clientPackageName, clientUid)) {
        // Returns a root ID that clients can use with onLoadChildren() to retrieve
        // the content hierarchy.
        MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)
    } else {
        // Clients can connect, but this BrowserRoot is an empty hierarchy
        // so onLoadChildren returns nothing. This disables the ability to browse for content.
        MediaBrowserServiceCompat.BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null)
    }
}

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // (Optional) Control the level of access for the specified package name.
    // You'll need to write your own logic to do this.
    if (allowBrowsing(clientPackageName, clientUid)) {
        // Returns a root ID that clients can use with onLoadChildren() to retrieve
        // the content hierarchy.
        return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
    } else {
        // Clients can connect, but this BrowserRoot is an empty hierarchy
        // so onLoadChildren returns nothing. This disables the ability to browse for content.
        return new BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null);
    }
}

ในบางกรณี คุณอาจต้องการควบคุมว่าใครจะเชื่อมต่อได้บ้าง ไปยัง MediaBrowserService วิธีหนึ่งคือการใช้รายการควบคุมการเข้าถึง (ACL) ซึ่งระบุการเชื่อมต่อที่ได้รับอนุญาต หรือแจกแจงรายการ การเชื่อมต่อที่ควรห้าม ตัวอย่างของวิธีติดตั้งใช้งาน ACL ซึ่งอนุญาตให้มีการเชื่อมต่อที่เฉพาะเจาะจง โปรดดู PackageValidator ใน Universal Android Music Player แอปตัวอย่าง

คุณควรพิจารณาระบุลำดับชั้นเนื้อหาที่แตกต่างกันโดยขึ้นอยู่กับ ประเภทของลูกค้าที่ทำการค้นหา โดยเฉพาะอย่างยิ่ง Android Auto จำกัดวิธีการ ผู้ใช้โต้ตอบกับแอปเสียง สำหรับข้อมูลเพิ่มเติม โปรดดู การเล่นเสียงสำหรับ อัตโนมัติ คุณ ให้ดู clientPackageName ขณะเชื่อมต่อเพื่อระบุไคลเอ็นต์ และแสดง BrowserRoot ที่แตกต่างกันโดยขึ้นอยู่กับไคลเอ็นต์ (หรือ rootHints) หากมี)

กำลังสื่อสารเนื้อหากับ onLoadChildren()

หลังจากที่ไคลเอ็นต์เชื่อมต่อแล้ว ไคลเอ็นต์จะข้ามลำดับชั้นเนื้อหาได้โดยการเรียกใช้ MediaBrowserCompat.subscribe() ซ้ำๆ เพื่อสร้างการแสดง UI ในเครื่อง เมธอด subscribe() จะส่ง Callback onLoadChildren() ไปยังบริการ ซึ่งแสดงรายการออบเจ็กต์ MediaBrowser.MediaItem

MediaItem แต่ละรายการมีสตริงรหัสที่ไม่ซ้ำกัน ซึ่งเป็นโทเค็นแบบทึบ เมื่อลูกค้าต้องการเปิดเมนูย่อยหรือเล่นรายการ ระบบจะส่งรหัส บริการของคุณมีหน้าที่ในการเชื่อมโยงรหัสกับโหนดเมนูหรือรายการเนื้อหาที่เหมาะสม

การใช้งาน onLoadChildren() อย่างง่ายอาจมีลักษณะเช่นนี้

Kotlin

override fun onLoadChildren(
        parentMediaId: String,
        result: MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>>
) {
    //  Browsing not allowed
    if (MY_EMPTY_MEDIA_ROOT_ID == parentMediaId) {
        result.sendResult(null)
        return
    }

    // Assume for example that the music catalog is already loaded/cached.

    val mediaItems = emptyList<MediaBrowserCompat.MediaItem>()

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID == parentMediaId) {
        // Build the MediaItem objects for the top level,
        // and put them in the mediaItems list...
    } else {
        // Examine the passed parentMediaId to see which submenu we're at,
        // and put the children of that menu in the mediaItems list...
    }
    result.sendResult(mediaItems)
}

Java

@Override
public void onLoadChildren(final String parentMediaId,
    final Result<List<MediaItem>> result) {

    //  Browsing not allowed
    if (TextUtils.equals(MY_EMPTY_MEDIA_ROOT_ID, parentMediaId)) {
        result.sendResult(null);
        return;
    }

    // Assume for example that the music catalog is already loaded/cached.

    List<MediaItem> mediaItems = new ArrayList<>();

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {
        // Build the MediaItem objects for the top level,
        // and put them in the mediaItems list...
    } else {
        // Examine the passed parentMediaId to see which submenu we're at,
        // and put the children of that menu in the mediaItems list...
    }
    result.sendResult(mediaItems);
}

หมายเหตุ: MediaItem ออบเจ็กต์ที่ส่งโดย MediaBrowserService ไม่ควรมีไอคอนบิตแมป โปรดใช้ Uri แทนโดยการโทรออก วันที่ setIconUri() เมื่อคุณสร้าง MediaDescription สำหรับแต่ละรายการ

สำหรับตัวอย่างวิธีใช้ onLoadChildren() โปรดดูแอปตัวอย่าง Universal Android Music Player

วงจรชีวิตบริการเบราว์เซอร์สื่อ

ลักษณะการทำงานของบริการ Android จะขึ้นอยู่กับว่าบริการนั้นเริ่มต้นแล้วหรือมีการเชื่อมโยงกับไคลเอ็นต์อย่างน้อย 1 ราย หลังจากสร้างบริการแล้ว บริการจะเริ่มต้น ผูกกับ หรือทั้ง 2 อย่างก็ได้ ในทุกสถานะเหล่านี้ โทรศัพท์ทำงานได้อย่างสมบูรณ์และสามารถทำงานตามที่ออกแบบมาได้ สิ่งที่แตกต่างคือระยะเวลาของบริการ บริการที่มีผลผูกพันจะไม่ถูกทำลายจนกว่าไคลเอ็นต์ที่ผูกไว้ทั้งหมดจะยกเลิกการเชื่อมโยง บริการเริ่มต้นสามารถหยุดและทำลายได้อย่างชัดแจ้ง (หากไม่มีการเชื่อมโยงกับไคลเอ็นต์ใดๆ อีกต่อไป)

เมื่อ MediaBrowser ที่ทำงานในกิจกรรมอื่นเชื่อมต่อกับ MediaBrowserService ระบบจะเชื่อมโยงกิจกรรมกับบริการ ทำให้บริการมีผลผูกพัน (แต่ยังไม่เริ่มต้น) ลักษณะการทำงานเริ่มต้นนี้มีให้ในตัว MediaBrowserServiceCompat

บริการที่มีการเชื่อมโยงเท่านั้น (และไม่ได้เริ่มต้น) จะถูกทำลายเมื่อไคลเอ็นต์ทั้งหมดยกเลิกการเชื่อมโยง หากกิจกรรม UI ถูกตัดการเชื่อมต่อในจุดนี้ บริการจะถูกทำลาย แต่ไม่ต้องกังวลหากคุณยังไม่ได้เปิดเพลงใดๆ อย่างไรก็ตาม เมื่อเริ่มเล่น ผู้ใช้น่าจะต้องการฟังเพลงต่อแม้ว่าจะเปลี่ยนแอปแล้วก็ตาม คุณไม่ต้องการทำลายโปรแกรมเล่นเมื่อคุณยกเลิกการเชื่อมโยง UI เพื่อทำงานกับแอปอื่น

ด้วยเหตุนี้ คุณจึงต้องมั่นใจว่าบริการได้เริ่มทำงานเมื่อเริ่มต้น ให้เล่นโดยโทรไปที่ startService() ต บริการที่เริ่มต้นจะต้องหยุดทำงานอย่างชัดเจน ไม่ว่าจะมีการเชื่อมโยงไว้หรือไม่ก็ตาม ช่วงเวลานี้ ทำให้มั่นใจได้ว่าโปรแกรมเล่นของคุณจะทำงานได้อย่างต่อเนื่องแม้ว่า UI ที่ใช้ควบคุม ยกเลิกการเชื่อมโยง

หากต้องการหยุดบริการที่เริ่มต้น โปรดโทรไปที่ Context.stopService() หรือ stopSelf() ระบบจะหยุดและทำลายบริการโดยเร็วที่สุด อย่างไรก็ตาม หากมีลูกค้าอย่างน้อย 1 รายที่ยังคงผูกกับบริการ การเรียกใช้ให้หยุดบริการจะล่าช้าจนกว่าไคลเอ็นต์ทั้งหมดจะยกเลิกการเชื่อมโยง

วงจรของ MediaBrowserService จะควบคุมโดยวิธีสร้าง จำนวนไคลเอ็นต์ที่เชื่อมโยงกับเซสชัน และการโทรที่ได้รับจาก Callback ของเซสชันสื่อ โดยสรุปได้ดังนี้

  • บริการสร้างขึ้นเมื่อเริ่มต้นตอบสนองต่อปุ่มสื่อ หรือเมื่อกิจกรรมเชื่อมโยงกับบริการ (หลังจากเชื่อมต่อผ่าน MediaBrowser)
  • Callback ของเซสชันสื่อ onPlay() ควรมีโค้ดที่เรียกใช้ startService() การดำเนินการนี้จะทำให้บริการเริ่มต้นและทำงานต่อไปแม้ว่ากิจกรรม MediaBrowser ของ UI ทั้งหมดที่เชื่อมโยงกับบริการดังกล่าวจะยกเลิกการเชื่อมโยงแล้วก็ตาม
  • Callback onStop() ควรโทรหา stopSelf() หากเริ่มต้นบริการแล้ว การดำเนินการนี้จะหยุดลง นอกจากนี้ บริการจะถูกทำลายหากไม่มีกิจกรรมที่เชื่อมโยงกับบริการ มิฉะนั้น บริการจะยังคงมีผลจนกว่ากิจกรรมทั้งหมดจะยกเลิกการเชื่อมโยง (หากได้รับการเรียก startService() ครั้งต่อๆ ไปก่อนที่บริการจะถูกทำลาย การแวะพักที่รอดำเนินการจะถูกยกเลิก)

โฟลว์ชาร์ตต่อไปนี้แสดงการจัดการวงจรของบริการ ตัวนับตัวแปรจะติดตามจำนวนไคลเอ็นต์ที่เข้าเงื่อนไข

วงจรบริการ

การใช้การแจ้งเตือน MediaStyle กับบริการที่ทำงานอยู่เบื้องหน้า

เมื่อเปิดบริการ บริการควรทำงานอยู่ในเบื้องหน้า ซึ่งจะช่วยให้ระบบทราบว่าบริการกำลังทำงานที่เป็นประโยชน์ และไม่ควรหยุดทำงานหากระบบมีหน่วยความจำต่ำ บริการที่ทำงานอยู่เบื้องหน้าต้องแสดงการแจ้งเตือนเพื่อให้ผู้ใช้รับรู้และสามารถเลือกควบคุมได้ Callback onPlay() ควรให้ความสำคัญกับบริการที่ทำงานอยู่เบื้องหน้า (โปรดทราบว่านี่เป็นความหมายพิเศษของ "พื้นหน้า" ในขณะที่ Android จะพิจารณาบริการในเบื้องหน้าเพื่อวัตถุประสงค์ในการจัดการกระบวนการ สำหรับผู้ใช้ที่เล่นวิดีโออยู่ในเบื้องหลังและในแอปอื่นบางส่วนจะแสดงอยู่ใน "เบื้องหน้า" บนหน้าจอ)

เมื่อบริการทำงานอยู่ในเบื้องหน้า บริการดังกล่าวต้องแสดงการแจ้งเตือน พร้อมกับตัวควบคุมการรับส่งข้อมูลอย่างน้อย 1 รายการ โดยการแจ้งเตือนควรมีข้อมูลที่เป็นประโยชน์จากข้อมูลเมตาของเซสชันด้วย

สร้างและแสดงการแจ้งเตือนเมื่อโปรแกรมเล่นเริ่มเล่น ตำแหน่งที่ดีที่สุดที่ทำได้คือภายในเมธอด MediaSessionCompat.Callback.onPlay()

ตัวอย่างด้านล่างใช้เมธอด NotificationCompat.MediaStyle, ซึ่งออกแบบมาสำหรับแอปสื่อ ซึ่งจะแสดงวิธีสร้างการแจ้งเตือนที่แสดงข้อมูลเมตาและการควบคุมการรับส่งข้อมูล วิธีการที่สะดวก getController() ช่วยให้คุณสร้างตัวควบคุมสื่อจากเซสชันสื่อได้โดยตรง

Kotlin

// Given a media session and its context (usually the component containing the session)
// Create a NotificationCompat.Builder

// Get the session's metadata
val controller = mediaSession.controller
val mediaMetadata = controller.metadata
val description = mediaMetadata.description

val builder = NotificationCompat.Builder(context, channelId).apply {
    // Add the metadata for the currently playing track
    setContentTitle(description.title)
    setContentText(description.subtitle)
    setSubText(description.description)
    setLargeIcon(description.iconBitmap)

    // Enable launching the player by clicking the notification
    setContentIntent(controller.sessionActivity)

    // Stop the service when the notification is swiped away
    setDeleteIntent(
            MediaButtonReceiver.buildMediaButtonPendingIntent(
                    context,
                    PlaybackStateCompat.ACTION_STOP
            )
    )

    // Make the transport controls visible on the lockscreen
    setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

    // Add an app icon and set its accent color
    // Be careful about the color
    setSmallIcon(R.drawable.notification_icon)
    color = ContextCompat.getColor(context, R.color.primaryDark)

    // Add a pause button
    addAction(
            NotificationCompat.Action(
                    R.drawable.pause,
                    getString(R.string.pause),
                    MediaButtonReceiver.buildMediaButtonPendingIntent(
                            context,
                            PlaybackStateCompat.ACTION_PLAY_PAUSE
                    )
            )
    )

    // Take advantage of MediaStyle features
    setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle()
            .setMediaSession(mediaSession.sessionToken)
            .setShowActionsInCompactView(0)

            // Add a cancel button
            .setShowCancelButton(true)
            .setCancelButtonIntent(
                    MediaButtonReceiver.buildMediaButtonPendingIntent(
                            context,
                            PlaybackStateCompat.ACTION_STOP
                    )
            )
    )
}

// Display the notification and place the service in the foreground
startForeground(id, builder.build())

Java

// Given a media session and its context (usually the component containing the session)
// Create a NotificationCompat.Builder

// Get the session's metadata
MediaControllerCompat controller = mediaSession.getController();
MediaMetadataCompat mediaMetadata = controller.getMetadata();
MediaDescriptionCompat description = mediaMetadata.getDescription();

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);

builder
    // Add the metadata for the currently playing track
    .setContentTitle(description.getTitle())
    .setContentText(description.getSubtitle())
    .setSubText(description.getDescription())
    .setLargeIcon(description.getIconBitmap())

    // Enable launching the player by clicking the notification
    .setContentIntent(controller.getSessionActivity())

    // Stop the service when the notification is swiped away
    .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(context,
       PlaybackStateCompat.ACTION_STOP))

    // Make the transport controls visible on the lockscreen
    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

    // Add an app icon and set its accent color
    // Be careful about the color
    .setSmallIcon(R.drawable.notification_icon)
    .setColor(ContextCompat.getColor(context, R.color.primaryDark))

    // Add a pause button
    .addAction(new NotificationCompat.Action(
        R.drawable.pause, getString(R.string.pause),
        MediaButtonReceiver.buildMediaButtonPendingIntent(context,
            PlaybackStateCompat.ACTION_PLAY_PAUSE)))

    // Take advantage of MediaStyle features
    .setStyle(new MediaStyle()
        .setMediaSession(mediaSession.getSessionToken())
        .setShowActionsInCompactView(0)

        // Add a cancel button
       .setShowCancelButton(true)
       .setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(context,
           PlaybackStateCompat.ACTION_STOP)));

// Display the notification and place the service in the foreground
startForeground(id, builder.build());

เมื่อใช้การแจ้งเตือน MediaStyle โปรดระมัดระวังการทำงานของการแจ้งเตือนเหล่านี้ การตั้งค่า NotificationCompat:

  • เมื่อคุณใช้ setContentIntent() บริการของคุณจะเริ่มต้นโดยอัตโนมัติเมื่อมีการแจ้งเตือน ซึ่งเป็นฟีเจอร์ที่มีประโยชน์
  • อยู่ในเกณฑ์ "ไม่น่าเชื่อถือ" สถานการณ์ เช่น หน้าจอล็อก ระดับการเข้าถึงเริ่มต้นสำหรับเนื้อหาการแจ้งเตือนคือ VISIBILITY_PRIVATE คุณอาจต้องการดู ตัวควบคุมการนำส่งบนหน้าจอล็อก ดังนั้น VISIBILITY_PUBLIC จึงเป็นตัวช่วยที่ต้องใช้
  • โปรดระมัดระวังเมื่อตั้งค่าสีพื้นหลัง ในการแจ้งเตือนปกติใน Android เวอร์ชัน 5.0 ขึ้นไป สีจะมีผลเฉพาะกับพื้นหลังของ ไอคอนแอปขนาดเล็ก แต่สำหรับการแจ้งเตือน MediaStyle ก่อน Android 7.0 สี จะใช้สำหรับพื้นหลังของการแจ้งเตือนทั้งหมด ทดสอบสีพื้นหลัง เริ่ม อ่อนโยนต่อดวงตาและหลีกเลี่ยงการใช้สีที่สว่างมากหรือสีฟลูออเรสเซนต์

การตั้งค่าเหล่านี้จะใช้ได้เฉพาะเมื่อคุณใช้ NotificationCompat.MediaStyle อยู่เท่านั้น

  • ใช้ setMediaSession() เพื่อเชื่อมโยงการแจ้งเตือนกับเซสชันของคุณ การดำเนินการนี้ช่วยให้แอปของบุคคลที่สาม และอุปกรณ์ที่ใช้ร่วมกันเพื่อเข้าถึงและควบคุมเซสชัน
  • ใช้ setShowActionsInCompactView() เพื่อเพิ่มการดำเนินการสูงสุด 3 อย่างที่จะแสดง contentView ขนาดมาตรฐานของการแจ้งเตือน (นี่คือปุ่มหยุดชั่วคราว ที่ระบุ)
  • ใน Android 5.0 (API ระดับ 21) ขึ้นไป คุณสามารถปัดการแจ้งเตือนออกไปเพื่อหยุด เมื่อบริการไม่ได้ทำงานอยู่ในเบื้องหน้าอีกต่อไป คุณไม่สามารถทำ ในเวอร์ชันก่อนหน้า เพื่ออนุญาตให้ผู้ใช้นำการแจ้งเตือนออกและหยุดการเล่น ก่อน Android 5.0 (API ระดับ 21) คุณสามารถเพิ่มปุ่มยกเลิก การแจ้งเตือนโดยโทรไปที่ setShowCancelButton(true) และ setCancelButtonIntent()

เมื่อเพิ่มปุ่มหยุดชั่วคราวและยกเลิก คุณจะต้องมี PendingIntent เพื่อแนบ การดำเนินการเล่น เมธอด MediaButtonReceiver.buildMediaButtonPendingIntent() มีหน้าที่แปลง จากการทำงาน PlayState ลงใน PendingIntent