16、车载媒体应用:MediaSession与MediaBrowserService;CarMediaService与媒体源管理;本地与在线音乐播放;视频播放与投屏
车载娱乐系统里,媒体应用是用户最常打交道的模块。开车时听歌、等人时看视频,这些场景我们每天都在面对。今天我就把这块的核心知识点拆开揉碎了讲给你听。
核心知识点一览
- MediaSession 与 MediaBrowserService 的配对使用
- CarMediaService 如何管理多个媒体源
- 本地音乐 vs 在线音乐的播放差异
- 视频播放与投屏的技术选型
16.1 MediaSession 与 MediaBrowserService
这两个东西是 Android 媒体框架的基石。说白了,MediaSession 是「播放器对外暴露的控制接口」,MediaBrowserService 是「媒体内容目录的提供者」。
我刚开始做车载媒体时,犯过一个低级错误——直接把 MediaPlayer 实例暴露给 UI 层。结果呢?UI 和播放逻辑耦合得死死的,换个播放器就得改一堆代码。后来老老实实用 MediaSession,真香。
MediaSession 的核心职责
- 接收外部控制命令(播放、暂停、下一曲、快进等)
- 向系统广播当前播放状态(正在播放哪首歌、进度多少)
- 配合锁屏、通知栏、语音助手等系统组件
// 创建 MediaSession 的典型写法
MediaSessionCompat mediaSession = new MediaSessionCompat(context, TAG);
mediaSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
// 我习惯在这里先检查当前是否有待播放的媒体项
if (currentMediaId != null) {
player.play();
}
}
@Override
public void onPause() {
player.pause();
}
@Override
public void onSkipToNext() {
loadMedia(nextMediaId);
}
});
mediaSession.setActive(true);
个人经验:MediaSession 的 Callback 是在 binder 线程回调的,不要在里边直接操作 UI。我曾经在这踩过坑,导致界面刷新延迟。建议用 Handler 切回主线程。
MediaBrowserService 的作用
它负责向系统提供媒体库的层级结构。比如你的音乐 App 里有「本地音乐」「在线音乐」「我的收藏」这些分类,MediaBrowserService 就是把这些目录暴露出去的桥梁。
public class MusicBrowserService extends MediaBrowserServiceCompat {
@Override
public void onCreate() {
super.onCreate();
// 初始化媒体源管理器
mediaSourceManager = new MediaSourceManager(this);
}
@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
Bundle rootHints) {
// 这里可以做权限校验
return new BrowserRoot("root", null);
}
@Override
public void onLoadChildren(String parentMediaId,
Result<List<MediaBrowserCompat.MediaItem>> result) {
// 根据 parentMediaId 返回子媒体项
result.sendResult(mediaSourceManager.getChildren(parentMediaId));
}
}
你想想看,如果没有 MediaBrowserService,每个 App 都得自己实现一套媒体目录管理逻辑,系统也没法统一调度。Google 设计这套框架,说白了就是为了标准化。
16.2 CarMediaService 与媒体源管理
车载系统和手机不一样。手机上一个音乐 App 只管自己的数据,但车载上可能有多个媒体源:USB 音乐、蓝牙音乐、在线音乐、收音机……这些源需要统一管理。
CarMediaService 就是 Android Automotive 专门干这事的。它负责注册、切换、优先级管理多个媒体源。
| 媒体源类型 | 优先级 | 典型场景 |
|---|---|---|
| 紧急广播 | 最高 | 安全预警、交通信息 |
| 导航提示 | 高 | 导航语音播报 |
| 电话 | 高 | 蓝牙通话 |
| 媒体播放 | 普通 | 音乐、视频 |
| 系统音效 | 低 | 按键音、提示音 |
注意:媒体源切换时一定要做淡入淡出处理。我曾经见过一个项目,切换源时直接咔一声切过去,用户体验极差。后来加了 200ms 的交叉淡入淡出,效果好了很多。
16.3 本地与在线音乐播放
本地音乐和在线音乐,技术实现上差别不小。我分别说说。
本地音乐播放
核心是 MediaPlayer 或 ExoPlayer。本地文件路径清晰,不需要处理网络波动。但要注意几个点:
- 文件格式兼容性:MP3、FLAC、WAV、AAC 都得支持
- 元数据读取:用 MediaMetadataRetriever 提取专辑封面、歌手、时长
- 存储权限:Android 10 以后要用 MediaStore API
// 读取本地音乐元数据
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(context, uri);
String title = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_TITLE);
String artist = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_ARTIST);
// 获取专辑封面
byte[] cover = retriever.getEmbeddedPicture();
} finally {
retriever.release();
}
在线音乐播放
在线播放的核心挑战是网络。我建议用 ExoPlayer,它对自适应码率(HLS、DASH)支持很好。
- 缓存策略:边播边缓存,断网时还能听
- 预加载:用户切歌时提前缓冲下一首
- 错误重试:网络波动时自动重试,别直接崩掉
避坑指南:在线音乐播放时,别忘了处理音频焦点。我曾经遇到过导航语音播报时音乐没暂停,两个声音混在一起,用户差点把车机砸了。用 AudioManager.requestAudioFocus() 就能解决。
16.4 视频播放与投屏
车载视频播放比音乐复杂得多。原因很简单——开车时不能看视频,但停车时可以。所以视频播放要跟车辆状态绑定。
视频播放的技术选型
- ExoPlayer:首选,支持硬解码,性能好
- VideoView:简单场景可以用,但定制能力弱
- 自定义 SurfaceView + MediaCodec:需要极致性能时用
// 车辆状态监听,控制视频播放
VehiclePropertyManager propertyManager =
new VehiclePropertyManager(context);
propertyManager.getProperty(
VehicleProperty.PARKING_BRAKE_ON,
new VehiclePropertyManager.VehiclePropertyListener() {
@Override
public void onPropertyChange(VehiclePropValue value) {
boolean isParked = value.getValue() == 1;
if (isParked) {
videoPlayer.start();
} else {
videoPlayer.pause();
// 我习惯在这里显示一个提示:"行驶中已暂停视频"
showDrivingNotice();
}
}
});
投屏功能
投屏分两种:手机投屏到车机(如 Android Auto、CarPlay),以及车机投屏到后排屏幕。
- Android Auto:走 MediaSession 协议,车机作为客户端
- Miracast / DLNA:无线投屏,延迟较高
- 有线投屏:USB-C 转 HDMI,延迟低,适合视频
核心原则:无论哪种投屏方式,都要保证驾驶安全。视频内容只能在驻车时显示,投屏时也要遵守这个规则。这是法规要求,不是技术选择。
嗯,关于车载媒体应用的核心内容就这些。MediaSession 和 MediaBrowserService 是基础,CarMediaService 是车载特有的管理机制,本地和在线播放各有侧重,视频播放要跟车辆状态联动。把这些理清楚了,车载媒体应用的骨架就有了。