浅谈 Media3 (一)

查看 25|回复 0
作者:AkaneTan   

   
一、Media3 的前世今生
Jetpack Media3 是媒体库的新家,为 Android 应用程序带来丰富的音频和视觉体验。 Media3 提供了一个具有强大的定制能力、可靠性极高和基于设备优化的简单架构,以消除碎片带来的复杂性。
Android Developers 文档
今年谷歌为 Media3, 也就是安卓最新的媒体播放实现库推出了第一个稳定版本。Media3 比起先前的 Media2, Exoplayer 有着极高的可定制性。
在 Media3 这样的库发布之前之前写一个播放器是非常困难的qwq 笔者之前的项目使用的是远古库 MediaPlayer, 不仅要手动处理播放清单、随机等逻辑, 和系统交互的 MediaSession 通知也需要自己构建。更不用提 MediaPlayer 还会因为各种奇奇怪怪的理由罢工 2333
Media3 为了规范播放器的架构将 Playback Notification / Resumption 的实现都包装在了一起, 让实现一个高效且标准的播放器非常容易。
二、Media3 的美好愿景
首先让我们来简单了解一下现在市面上大部分音乐/视频播放器所使用的软件结构:
如上面的简例所示,服务层包含了 Player 本体、与 Android 系统 / 活动层 对接的 MediaSession。而活动层包含了与服务对接的媒体控制器还有呈现给用户看的UI界面。
但有些没有实战过的读者可能会问了:
这不是很简单吗???
实际上上面的示意图省去了很多部分,比如 MediaSession 和 Player 之间对接的控制器,媒体控制器还有自己对于播放的接口,需要UI去实现。这导致了一个非常大的问题,结构太过于复杂。代码的复杂性只会导致更多的Bug。
甚至在实现控制器的时候还有多个库可以使用 (ExoPlayer, Media2),这往往会给开发者造成困惑。
Media3 的处理是这样的:
在 Media3 的理想结构下, 四个部件都不用做连接器处理。它们都直接实现通用播放器的接口。这不仅省去了写一大堆连接器的麻烦, 而且让代码更加易于维护。可能之前我们用 MediaPlayer 实例的时候需要自己处理 Activity 与 Service 之间的通信, 但在 Media3 中, 这些都已经被预制的通用播放器解决了。
你还会发现, 当你采用了 Media3 的架构来写播放器的时候, UI 层和服务层是完全分离的。这意味着更少的错误和更好的独立性。对于一个存活的 Service, 可以有多个播放器界面存在。
同时, Media3 还整合了 ExoPlayer2 作为它可选的播放器实现。
在 Android 13 中, 安卓推送了一项新功能: Playback Resumption (也就是前文所说的播放恢复)。当使用 Playback Resumption 的时候, 应用程序生成的 Media Control 通知 应该常驻于快速设置的面板上, 这样即使播放器根本不在运行, 使用 Media Control 也能迅速开始播放退出时的播放清单。
一些比较知名的播放器,比如自称安卓第一的 Retro Music Player, 还有 Oto Music 都没有把 Playback Resumption 做好。事实上,根据笔者的观察, 市面上除了笔者自己的 Gramophone 和 Spotify 之外似乎没有任何一个播放器正确的植入了 Playback Resumption。这导致 Playback Resumption 这一功能就等于作废, 停驻的 Media Control 通知并不会迅速恢复播放, 只会 ”假死”。
幸运的是, Media3 发现了这一现状, 并且提供了相应的接口方便的供开发者来实现 Playback Resumption。
三、实现
1° Service
如上面的代码段, 这便是一个最简单的 Media3 播放器实例了。我们这里使用的是实现播放功能最简单的 MediaSessionService, 但通常情况下最好使用 MediaLibraryService。MediaLibraryService 能够为其他的使用暴露播放器的媒体库 (Android Auto, 语音助手)。
让我们逐行分解。
1.MediaSession
我们在这里定义了一个 mediaSession。这个 session 将作为 Activity 的 UI 层与 Service 层交互的凭证。 Activity 层可以凭借这个 Session 来与播放器通讯, 添加播放清单等。
2.
我们在这里创建了一个 ExoPlayer 的实例。请注意,此处的 ExoPlayer 非彼 ExoPlayer。 ExoPlayer 作为 Media3 的一部分被包含在了 Media3 当中。
而 AudioAttributes 是我们加给 ExoPlayer 的媒体信息,这样当其他的应用需要播放媒体的时候, ExoPlayer 便会自动暂停,当别的应用释放媒体焦点后 ExoPlayer 再自动获取。之前需要用到 AudioManager 来手动处理这一点,现在可以直接通过 AudioAttributes 直接解决。
2° Activity
活动端的实现很简单,我们只需要在 onStart 中添加以下获取 session 的方法即可。
在这里我们获取了一个 controllerFuture,而我们可以通过它来获取一个 MediaController 来与服务端交互。
举个例子,假如我们想要暂停播放的话,就可以用 player.pause() 来实现。如果以前我们想要更新 UI 内容的话可能会要用到 Handler 或者 Thread。现在我们只需要给 Player 新增一个回调即可:
别忘了添加回调:
有关 Media Resumption 的功能我们留到下一篇文章。
如果对实际实现有兴趣的读者可以在 GitHub 上搜索我的用户名,名为 Gramophone 的仓库储存的便是我用 Media3 新写的播放器。








播放器, 的是, 在这里

您需要登录后才可以回帖 登录 | 立即注册

返回顶部