资讯专栏INFORMATION COLUMN

Android开发音视频应用之构建媒体浏览器服务

LMou / 2839人阅读

摘要:构建媒体浏览器服务您的应用必须在其清单中声明带有。只有绑定并且未启动的服务在其所有客户端解除绑定时销毁。系统会尽快停止并销毁服务。该回调应该把服务的前景。通知还应包括会话元数据中的有用信息。它显示了如何构建显示元数据和传输控件的通知。

构建媒体浏览器服务
您的应用必须MediaBrowserService在其清单中声明带有intent-filter。您可以选择自己的服务名称; 在以下示例中,它是“MediaPlaybackService”。


  
    
  

注意:推荐的实现MediaBrowserService 是MediaBrowserServiceCompat。这是在media-compat支持库中定义的 。在整个页面中,术语“MediaBrowserService”指的是of的一个实例MediaBrowserServiceCompat。

初始化媒体会话
当服务收到onCreate()生命周期回调方法时,它应该执行以下步骤:

创建并初始化媒体会话
设置媒体会话回调
设置媒体会话令牌
onCreate()下面的代码演示了以下步骤:

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 mMediaSession;
    private PlaybackStateCompat.Builder mStateBuilder;

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

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

        // Enable callbacks from MediaButtons and TransportControls
        mMediaSession.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
        mStateBuilder = new PlaybackStateCompat.Builder()
                            .setActions(
                                PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PLAY_PAUSE);
        mMediaSession.setPlaybackState(mStateBuilder.build());

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

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

管理客户连接
MediaBrowserService有两种处理客户端连接的方法: onGetRoot()控制对服务的访问,并 onLoadChildren() 为客户端提供构建和显示MediaBrowserService内容层次结构菜单的能力。

使用控制客户端连接 onGetRoot()

该onGetRoot()方法返回内容层次结构的根节点。如果方法返回null,则拒绝连接。

要允许客户端连接到您的服务并浏览其媒体内容,onGetRoot()必须返回一个非空的BrowserRoot,它是一个表示您的内容层次结构的根ID。

要允许客户端在不浏览的情况下连接到MediaSession,onGetRoot()仍必须返回非null的BrowserRoot,但根ID应表示空的内容层次结构。

典型的实现onGetRoot()可能如下所示:

@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 hierachy
        // so onLoadChildren returns nothing. This disables the ability to browse for content.
        return new BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null);
    }
}

在某些情况下,您可能希望实施白/黑名单方案来控制连接。有关白名单的示例,请参阅通用Android音乐播放器示例应用程序中的PackageValidator类。

注意:您应该考虑提供不同的内容层次结构,具体取决于进行查询的客户端类型。特别是,Android Auto会限制用户与音频应用的互动方式。有关更多信息,请参阅为自动播放音频。您可以查看clientPackageName连接时间以确定客户端类型,并BrowserRoot根据客户端(或者rootHints 如果有)返回不同的客户端类型。

与内容沟通内容 onLoadChildren()
在客户端连接之后,它可以通过重复调用MediaBrowserCompat.subscribe()来构建UI的本地表示来遍历内容层次结构。该subscribe()方法将回调发送onLoadChildren()到服务,该服务返回MediaBrowser.MediaItem对象列表。

每个MediaItem都有一个唯一的ID字符串,它是一个不透明的标记。当客户想要打开子菜单或播放项目时,它会传递ID。您的服务负责将ID与相应的菜单节点或内容项相关联。

一个简单的实现onLoadChildren()可能如下所示:

@Override
public void onLoadChildren(final String parentMediaId,
    final Result> 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 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(),请参阅MediaBrowserService和Universal Android Music Player示例应用程序。

媒体浏览器服务生命周期
Android 服务的行为取决于它是启动还是绑定到一个或多个客户端。创建服务后,可以启动,绑定或同时启用它。在所有这些状态中,它功能齐全,可以执行其设计的工作。不同之处在于服务存在多长时间。绑定的服务在其所有绑定的客户端解除绑定之前不会被销毁。可以显式停止和销毁已启动的服务(假设它不再绑定到任何客户端)。

当MediaBrowser另一个活动中的运行连接到a时MediaBrowserService,它会将活动绑定到服务,从而使服务绑定(但不启动)。此默认行为内置于MediaBrowserServiceCompat类中。

只有绑定(并且未启动)的服务在其所有客户端解除绑定时销毁。如果此时UI活动断开连接,则服务将被销毁。如果您还没有播放任何音乐,这不是问题。但是,当播放开始时,用户可能希望即使在切换应用后也能继续收听。当您取消绑定UI以使用其他应用程序时,您不希望销毁播放器。

因此,您需要确保在通过调用开始播放服务时启动该服务startService()。无论是否绑定,必须明确停止已启动的服务。这可确保即使控制UI活动解除绑定,您的播放器也会继续执行。

要停止已启动的服务,请致电Context.stopService()或stopSelf()。系统会尽快停止并销毁服务。但是,如果一个或多个客户端仍然绑定到该服务,则停止该服务的调用将延迟,直到其所有客户端解除绑定。

它的生命周期MediaBrowserService由创建方式,绑定到它的客户端数量以及从媒体会话回调接收的调用控制。总结一下:

该服务在响应媒体按钮或活动绑定到它(通过其连接后MediaBrowser)启动时创建。
媒体会话onPlay()回调应包括调用的代码startService()。这可确保服务启动并继续运行,即使MediaBrowser绑定到它的所有UI 活动都解除绑定。
该onStop()回调应该调用stopSelf()。如果服务已启动,则会停止该服务。此外,如果没有绑定的活动,服务将被销毁。否则,服务将保持绑定,直到其所有活动解除绑定。(如果startService()在销毁服务之前收到后续呼叫,则取消挂起停止。)
以下流程图演示了如何管理服务的生命周期。变量计数器跟踪绑定客户端的数量:

将MediaStyle通知与前台服务一起使用
当服务正在播放时,它应该在前台运行。这使系统知道服务正在执行有用的功能,如果系统内存不足,则不应该被杀死。前台服务必须显示通知,以便用户知道它并可以选择控制它。该onPlay()回调应该把服务的前景。(请注意,这是“前景”的特殊含义。虽然Android在前台考虑服务以进行流程管理,但是对于用户,播放器正在后台播放,而其他应用程序在“前景”中可见屏幕。)

当服务在前台运行时,它必须显示通知,理想情况下是一个或多个传输控件。通知还应包括会话元数据中的有用信息。

在播放器开始播放时构建并显示通知。这样做的最佳位置是MediaSessionCompat.Callback.onPlay()方法内部。

以下示例使用 NotificationCompat.MediaStyle专为媒体应用设计的。它显示了如何构建显示元数据和传输控件的通知。便捷方法 getController() 允许您直接从媒体会话创建媒体控制器。

// 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或更高版本的普通通知中,颜色仅应用于小应用程序图标的背景。但对于Android 7.0之前的MediaStyle通知,颜色用于整个通知背景。测试你的背景颜色。温柔的眼睛,避免极其明亮或荧光的颜色。

用于 setMediaSession() 将通知与您的会话相关联。这允许第三方应用和配套设备访问和控制会话。

用于 setMediaSession() 将通知与您的会话相关联。这允许第三方应用和配套设备访问和控制会话。

用于 setShowActionsInCompactView() 在通知的标准大小的contentView中添加最多3个操作。(此处指定了暂停按钮。)

在Android 5.0(API级别21)及更高版本中,一旦服务不再在前台运行,您可以滑动通知以停止播放器。您不能在早期版本中执行此操作。要允许用户在Android 5.0(API级别21)之前删除通知并停止播放,您可以通过调用 setShowCancelButton(true) 和在通知的右上角添加取消按钮 setCancelButtonIntent() 。

添加暂停和取消按钮时,您需要PendingIntent附加到播放操作。该方法 MediaButtonReceiver.buildMediaButtonPendingIntent() 执行将PlaybackState操作转换为PendingIntent的工作。

总结
写的一般,欢迎留言、私信指出问题与不足之处!如回复不及时可加入Android技术交流群:150923287 一起学习探讨Android开发技术!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/14674.html

相关文章

  • Android开发视频应用构建媒体览器服务

    摘要:构建媒体浏览器服务您的应用必须在其清单中声明带有。只有绑定并且未启动的服务在其所有客户端解除绑定时销毁。系统会尽快停止并销毁服务。该回调应该把服务的前景。通知还应包括会话元数据中的有用信息。它显示了如何构建显示元数据和传输控件的通知。 构建媒体浏览器服务您的应用必须MediaBrowserService在其清单中声明带有intent-filter。您可以选择自己的服务名称; 在以下示例...

    Honwhy 评论0 收藏0
  • Android开发视频应用构建媒体览器服务

    摘要:构建媒体浏览器服务您的应用必须在其清单中声明带有。只有绑定并且未启动的服务在其所有客户端解除绑定时销毁。系统会尽快停止并销毁服务。该回调应该把服务的前景。通知还应包括会话元数据中的有用信息。它显示了如何构建显示元数据和传输控件的通知。 构建媒体浏览器服务您的应用必须MediaBrowserService在其清单中声明带有intent-filter。您可以选择自己的服务名称; 在以下示例...

    Bryan 评论0 收藏0
  • Android开发视频应用构建媒体览器服务

    摘要:构建媒体浏览器服务您的应用必须在其清单中声明带有。只有绑定并且未启动的服务在其所有客户端解除绑定时销毁。系统会尽快停止并销毁服务。该回调应该把服务的前景。通知还应包括会话元数据中的有用信息。它显示了如何构建显示元数据和传输控件的通知。 构建媒体浏览器服务您的应用必须MediaBrowserService在其清单中声明带有intent-filter。您可以选择自己的服务名称; 在以下示例...

    Reducto 评论0 收藏0
  • 我的视频技术笔记路线图

    摘要:我的文章不同于其他作者,鲜少有手把手入门性质文章这方面我的师哥雷博士已有许多著作在前,多是以某一个技术点为主题,展开进行较为深入的介绍。 恍惚间发现自己的博客文章已...

    Riddler 评论0 收藏0
  • 览器android移动端视频互播技术实现

    摘要:再次,系统平台的视频画面播放功能采用播放框架实现,是基于的网络视频播放器,根据指定的视频流通道地址从流媒体服务器自动拉取格式视频流,并播放实时的视频画面。       手机端与平台之间的视频直播功能,主要通过集成多种开源视频框架以及采购第三方视频直播服务器产品来实现预定业务需求。视频直播对话功能的实现,主要经历了三个阶段:利用开源视频框架实现视频直播、采购第三方视频服务器实现视频直播、系统平...

    remcarpediem 评论0 收藏0

发表评论

0条评论

LMou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<