搭建媒体浏览器服务你的应用必须在其manifest中声明带有intent-filter的MediaBrowserService。您可以选择自己的服务名称;在下面的示例中,它是“MediaPlaybackService”。注:MediaBrowserService的推荐实现是MediaBrowserServiceCompat。这是在媒体兼容支持库中定义的。在整个页面中,术语“MediaBrowserService”指的是MediaBrowserServiceCompat的一个实例。初始化媒体会话当服务接收到onCreate()生命周期回调方法时,它应该执行以下步骤:创建并初始化媒体会话设置媒体会话回调设置媒体会话令牌onCreate()下面的代码演示了以下步骤:公共类MediaPlaybackService扩展MediaBrowserServiceCompat{privatestaticfinalStringMY_MEDIA_ROOT_ID="media_root_id";privatestaticfinalStringMY_EMPTY_MEDIA_ROOT_ID="empty_root_id";私有MediaSessionCompatmMediaSession;私有PlaybackStateCompat.BuildermStateBuilder;@OverridepublicvoidonCreate(){super.onCreate();//创建一个MediaSessionCompatmMediaSession=newMediaSessionCompat(context,LOG_TAG);//启用MediaButtons和TransportControls的回调mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS|MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);//使用ACTION_PLAY设置初始PlaybackState,以便媒体按钮可以启动播放器mStateBuilder=newPlaybackStateCompat.生成器().setActions(PlaybackStateCompat.ACTION_PLAY|PlaybackStateCompat.ACTION_PLAY_PAUSE);mMediaSession.setPlaybackState(mStateBuilder.build());//MySessionCallback()具有处理来自媒体控制器的回调的方法mMediaSession.setCallback(newMySessionCallback());//设置会话的令牌,以便客??户端活动可以与之通信。setSessionToken(mMediaSession.getSessionToken());}}管理客户端连接MediaBrowserService有两种处理客户端连接的方法:onGetRoot()控制对服务的访问,onLoadChildren()为客户端提供构建和显示MediaBrowserService内容层次菜单的能力。使用控件客户端连接onGetRoot()onGetRoot()方法返回内容层次结构的根节点。如果该方法返回null,则连接被拒绝。为了允许客户端连接到您的服务并浏览其媒体内容,onGetRoot()必须返回一个非空的BrowserRoot,它是代表您的内容层次结构的根ID。为了允许客户端在不浏览的情况下连接到MediaSession,onGetRoot()仍必须返回一个非空的BrowserRoot,但根ID应该代表一个空的内容层次结构。onGetRoot()的典型实现可能如下所示:@OverridepublicBrowserRootonGetRoot(StringclientPackageName,intclientUid,BundlerootHints){//(可选)控制指定包名称的访问级别。//你需要编写自己的逻辑来执行此操作。if(allowBrowsing(clientPackageName,clientUid)){//返回一个根ID,客户端可以使用它与onLoadChildren()一起检索//内容层次结构。返回新的BrowserRoot(MY_MEDIA_ROOT_ID,null);}else{//客户端可以连接,但是这个BrowserRoot是一个空的层次结构//所以onLoadChildren什么都不返回。这将禁用浏览内容的能力。返回新的BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID,null);}}在某些情况下,您可能希望实施白/黑名单方案来控制连接。有关白名单的示例,请参阅通用Android音乐播放器示例应用程序中的PackageValidator类。注意:您应该考虑根据进行查询的客户端类型提供不同的内容层次结构。特别是,AndroidAuto限制了用户与音频应用程序的交互方式。有关详细信息,请参阅自动播放音频。您可以在连接时查看clientPackageName以确定客户端类型,BrowserRoot会根据客户端(或rootHints,如果有的话)返回不同的客户端类型。使用onLoadChildren()内容传达内容客户端连接后,它可以通过重复调用MediaBrowserCompat.subscribe()来遍历内容层次结构,以构建UI的本机表示。subscribe()方法将回调onLoadChildren()发送到服务,该服务返回MediaBrowser.MediaItem对象的列表。每个MediaItem都有一个唯一的ID字符串,它是一个不透明的标记。当客户端想要打开子菜单或播放项目时,它会传递ID。您的服务负责将ID与相应的菜单节点或内容项相关联。onLoadChildren()的简单实现可能如下所示:@OverridepublicvoidonLoadChildren(finalStringparentMediaId,finalResult>result){//不允许浏览if(TextUtils.equals(MY_EMPTY_MEDIA_ROOT_ID,parentMediaId)){result.sendResult(空);返回;}//例如,假设音乐目录已经加载/缓存。ListmediaItems=newArrayList<>();//检查这是否是根菜单:if(MY_MEDIA_ROOT_ID.equals(parentMediaId)){//为顶层构建MediaItem对象,//并将它们放入mediaItems列表中...}else{//检查传递parentMediaId以查看我们所在的子菜单,//并将该菜单的子项放入mediaItems列表中...}result.sendResult(mediaItems);}注意:MediaBrowserService传递的MediaItem对象不应包含图标位图.使用Uri而不是在为onLoadChildren()生成MediaDescription的每个项目上调用setIconUri()有关如何实现它的示例,请参阅MediaBrowserService和通用Android音乐播放器示例应用程序。媒体浏览器服务生命周期Android服务的行为取决于它是否启动或绑定到一个或多个客户端。服务创建后,可以启动、绑定或同时启用。在所有这些状态下,它都功能齐全,并且按照设计的目的进行。不同之处在于服务存在的时间。在所有绑定的客户端都解除绑定之前,绑定服务不会被销毁。可以显式停止和销毁启动的服务(假设它不再绑定到任何客户端)。当在另一个活动中运行的MediaBrowser连接到MediaBrowserService时,它??将活动绑定到服务,导致服务绑定(但不启动)。此默认行为内置于MediaBrowserServiceCompat类中。只有绑定的(未启动的)服务在所有客户端解除绑定时才会被销毁。如果此时UI活动断开连接,该服务将被销毁。如果您还没有播放过任何音乐,这不是问题。但是,当播放开始时,用户可能希望在切换应用程序后继续收听。当您解除绑定UI以使用另一个应用程序时,您不想破坏播放器。因此,您需要确保通过调用startService()启动服务时服务已启动。无论绑定与否,启动的服务都必须显式停止。这确保即使控制UI活动未绑定,您的播放器也将继续执行。要停止已启动的服务,请调用Context.stopService()或stopSelf()。系统会尽快停止并销毁该服务。但是,如果一个或多个客户端仍然绑定到该服务,停止服务的调用将被延迟,直到它的所有客户端都解除绑定。它的生命周期MediaBrowserService由它的创建方式、绑定到它的客户端数量以及它从媒体会话回调中接收的调用来控制。总结一下:此服务是为响应媒体按钮或在活动绑定到它(通过其MediaBrowser)启动时创建的。媒体会话onPlay()回调应包含调用startService()的代码。这确保服务启动并继续运行,即使MediaBrowser绑定到它的所有UI活动都未绑定。onStop()回调应该调用stopSelf()。如果服务已经启动,则服务将被停止。此外,如果没有绑定的活动,服务将被销毁。否则,该服务将保持绑定状态,直到它的所有活动都解除绑定为止。(如果startService()在服务被销毁之前收到后续调用,则取消pendingstop。)下面的流程图演示了如何管理服务的生命周期。一个变量计数器跟踪绑定客户端的数量:将MediaStyle通知与前台服务一起使用当服务正在播放时,它应该在前台运行。这让系统知道该服务正在执行有用的功能,如果系统内存不足,不应将其终止。前台服务必须显示通知,以便用户知道它并可以选择控制它。onPlay()回调应该将服务带到前台。(请注意,这是“前台”的特殊含义。虽然Android出于进程管理的目的考虑前台服务,但对于用户而言,播放器在后台播放,而其他应用程序在“前台”有一个可见的屏幕。)当服务在前台运行时,它必须显示一个通知,最好是一个或多个传输控件。通知还应包括会话元数据中的有用信息。在播放器开始播放时构建并显示通知。执行此操作的最佳位置是在MediaSessionCompat.Callback.onPlay()方法内。以下示例使用专为媒体应用程序设计的NotificationCompat.MediaStyle。它展示了如何构建显示元数据和传输控制的通知。便捷方法getController()允许您直接从媒体会话创建媒体控制器。//给定媒体会话及其上下文(通常是包含会话的组件)//创建NotificationCompat.Builder//获取会话的元数据MediaControllerCompatcontroller=mediaSession.getController();MediaMetadataCompatmediaMetadata=controller.getMetadata();MediaDescriptionCompatdescription=mediaMetadata.getDescription();NotificationCompat.Builderbuilder=newNotificationCompat.Builder(context,channelId);builder//添加当前播放曲目的元数据.setContentTitle(description.getTitle()).setContentText(description.getSubtitle()).setSubText(description.getDescription()).setLargeIcon(description.getIconBitmap())//通过点击通知启动播放器.setContentIntent(controller.getSessionActivity())//当通知被滑动时停止服务.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(上下文,PlaybackStateCompat.ACTION_STOP))//使传输控件在锁屏上可见.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)//添加应用程序图标并设置其强调色//注意颜色.setSmallIcon(R.drawable.notification_icon).setColor(ContextCompat.getColor(context,R.color.primaryDark))//添加暂停按钮.addAction(newNotificationCompat.Action(R.drawable.pause,getString(R.string.pause),MediaButtonReceiver.buildMediaButtonPendingIntent(context,PlaybackStateCompat...(context,PlaybackStateCompat.ACTION_STOP)));//显示通知d将服务放在前台startForeground(id,builder.build());当使用MediaStyle通知时,请注意这些NotificationCompat设置的行为:当使用setContentIntent()时,您的服务会在单击通知时自动启动,这是在“不受信任”的情况下的一个方便功能,例如锁定屏幕,默认可见性通知内容为VISIBILITY_PRIVATE。您可能希望在锁定屏幕上看到传输控件,因此VISIBILITY_PUBLIC就可以了。设置背景颜色时要小心。在Android5.0或更高版本的正常通知中,颜色仅应用于小应用程序图标的背景。但对于Android7.0之前的MediaStyle通知,颜色用于整个通知背景。测试你的背景颜色。对于柔和的眼睛,请避免使用极其明亮或荧光的颜色。使用setMediaSession()将通知与您的会话相关联。这允许第三方应用程序和配套设备访问和控制会话。使用setMediaSession()将通知与您的会话相关联。这允许第三方应用程序和配套设备访问和控制会话。使用setShowActionsInCompactView()在通知的标准大小的contentView中添加最多3个操作。(此处指定暂停按钮。)在Android5.0(API级别21)及更高版本中,一旦服务不再在前台运行,您可以滑动通知以停止播放器。您不能在早期版本中执行此操作。要允许用户在Android5.0(API级别21)之前关闭通知并停止播放,您可以通过调用setShowCancelButton(true)和setCancelButtonIntent()在通知的右上角添加一个取消按钮。添加暂停和取消按钮时,您需要将PendingIntent附加到播放操作。MediaButtonReceiver.buildMediaButtonPendingIntent()方法负责将PlaybackState操作转换为PendingIntent。总结写的一般,欢迎留言、私信指出问题和不足!如果回复不及时,可以加入Android技术交流群:150923287一起学习讨论Android开发技术!