使用 Media3 ExoPlayer 创建基本的媒体播放器应用

Jetpack Media3 定义了一个 Player 接口,其中列出了基本功能 来播放视频和音频文件。ExoPlayer 是默认实现 该接口位于 Media3 中我们建议使用 ExoPlayer,因为它提供了 功能全面,涵盖大多数播放用例, 以处理您可能遇到的任何其他用例。ExoPlayer 还 摆脱设备和操作系统碎片化问题,让您的代码以一致的方式运行 在整个 Android 生态系统中ExoPlayer 包含:

本页将向您介绍构建播放 如需了解更多详情,您可以参阅关于 Media3 ExoPlayer

使用入门

首先,添加对 ExoPlayer、UI 和 Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.0"
implementation "androidx.media3:media3-ui:1.4.0"
implementation "androidx.media3:media3-common:1.4.0"

根据您的用例,您可能还需要 Media3、 (例如 exoplayer-dash),以 DASH 格式播放视频流。

请务必将 1.4.0 替换为您偏好的 库。您可以参阅版本说明 以查看最新版本。

创建媒体播放器

对于 Media3,您可以使用包含的 Player 实现 接口、ExoPlayer,您也可以构建自己的自定义实现。

创建 ExoPlayer

如需创建 ExoPlayer 实例,最简单的方法是如下所示:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

您可以通过onCreate() ActivityFragmentService

Builder包括 一系列您可能感兴趣的自定义选项,例如:

Media3 提供了一个 PlayerView 界面组件,您可以将该组件添加到应用的 布局文件。此组件封装了用于播放的 PlayerControlView 控件,SubtitleView 用于显示字幕,Surface 用于渲染 视频。

准备播放器

媒体内容添加到以下播放列表: 例如通过 setMediaItem()addMediaItem()。 然后,调用 prepare() 以 开始加载媒体并获取必要的资源。

您不应在应用处于前台之前执行这些步骤。如果您的 播放器位于 ActivityFragment 中,这意味着要为 onStart() 生命周期方法(在 API 级别 24 及更高级别上,或者 onResume()) Lifecycle 方法。对于 Service 中的播放器, 可以在 onCreate() 中进行准备。

控制播放器

播放器准备就绪后,您可以通过调用方法来控制播放。 例如:

界面组件(例如 PlayerViewPlayerControlView)将更新 会相应地发生调整

释放播放器

播放可能需要供应量有限的资源,例如视频 因此请务必调用 release() ,以便在不再需要该播放器时释放资源。

如果您的播放器处于 ActivityFragment 中,请在 onStop() 生命周期方法(在 API 级别 24 及更高级别上,或者 onPause()) 方法。对于 Service 中的播放器,您可以 在 onDestroy() 中释放。

使用媒体会话管理播放

在 Android 上,媒体会话提供了一种与媒体互动的标准化方式 来满足这些需求将媒体会话连接到播放器 让您可以在外部通告自己的媒体播放,并接收播放 来自外部来源的命令,例如用于 移动设备和大型设备上的系统媒体控件 屏幕设备。

如需使用媒体会话,请添加对 Media3 Session 模块的依赖项:

implementation "androidx.media3:media3-session:1.4.0"

创建媒体会话

您可以在初始化播放器后创建 MediaSession,如下所示:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 会自动将 Player 的状态与 MediaSession。这适用于所有 Player 实现,包括 ExoPlayerCastPlayer 或 自定义实现

向其他客户端授予控制权

客户端应用可以实现媒体控制器 来控制媒体会话的播放。要接收这些请求,请将 Callback 对象时, 构建您的 MediaSession

当控制器即将连接到您的媒体会话时, onConnect() 方法。您可以使用提供的 ControllerInfo 然后决定是否接受拒绝 请求。您可以在 Media3 Session 演示版应用中查看相关示例。

连接后,控制器即可向会话发送播放命令。通过 然后将这些命令委托给玩家。播放和播放列表 Player 接口中定义的命令由 会话。

例如,您可以使用其他回调方法处理对 自定义播放命令修改播放列表。这些回调同样包含一个 ControllerInfo 对象,因此您可以 可以逐个请求确定访问权限控制。

正在后台播放媒体内容

例如,在应用未在前台运行时继续播放媒体内容 播放音乐、有声读物或播客,即使用户未安装您的应用也无妨 您的 PlayerMediaSession 应封装在 前台服务。Media3 为 MediaSessionService 接口实现这一目的。

实现 MediaSessionService

创建一个扩展 MediaSessionService 的类,并实例化 onCreate() 生命周期方法中的 MediaSession

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Java

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

在清单中,包含 MediaSessionService intent 的 Service 类 过滤并请求 FOREGROUND_SERVICE 权限以运行前台 服务:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

最后,在您创建的类中,替换 onGetSession() 方法以控制 客户端访问您的媒体会话的权限。返回一个 MediaSession 以接受 连接请求,或返回 null 以拒绝请求。

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

连接到界面

现在,您的媒体会话位于 Service(与 ActivityFragment,那么您可以使用 MediaController 来关联 将它们放在一起在 ActivityFragmentonStart() 方法中,使用 在界面中,为您的 MediaSession 创建 SessionToken,然后使用 SessionToken 来构建 MediaController。构建 MediaController 异步执行。

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController 会实现 Player 接口,因此您可以使用同样的 方法(例如 play()pause())来控制播放。与其他类似 组件之前,请务必释放 MediaController。 例如 ActivityonStop() 生命周期方法,只需调用 MediaController.releaseFuture()

发布通知

需要前台服务才能发布处于活跃状态的通知。答 MediaSessionService会自动创建 MediaStyle通知: 以 MediaNotification 的形式提供给您。 要提供自定义通知,请创建一个 MediaNotification.ProviderDefaultMediaNotificationProvider.Builder共享 也可以创建提供程序接口的自定义实现添加您的 将提供商与您的 MediaSession 相关联,具体如下: setMediaNotificationProvider

宣传你的内容库

MediaLibraryServiceMediaSessionService 的基础上构建,通过允许客户端构建 浏览应用提供的媒体内容。客户端应用实现了 MediaBrowser即可互动 与您的MediaLibraryService共享。

实现 MediaLibraryService 与实现 MediaSessionService,只不过在 onGetSession() 中,您应返回 MediaLibrarySession,而不是 MediaSession。与 MediaSession.CallbackMediaLibrarySession.Callback包含 方法,这些方法允许浏览器客户端浏览您的 图书馆服务

MediaSessionService 类似,在MediaLibraryService 清单并请求 FOREGROUND_SERVICE 权限以运行前台 服务:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

上面的示例包含 MediaLibraryService 和 以及旧版 MediaBrowserService(为了向后兼容)。通过 额外的 intent 过滤器支持使用 MediaBrowserCompat API 的客户端应用 以便识别您的Service

借助 MediaLibrarySession,您可以在树中提供内容库 具有单个根 MediaItem。树中的每个 MediaItem 任意数量的子 MediaItem 节点。你可以提供其他根,或 不同的树。例如,您构建的树 如果客户再次寻找推荐的媒体项列表, 包含根 MediaItem 和单级子 MediaItem 节点, 而返回其他客户端应用的树可能代表 一个完整的内容库

创建 MediaLibrarySession

MediaLibrarySession 扩展了 MediaSession API,以添加内容浏览 API。与 MediaSession 回调MediaLibrarySession 回调 添加了如下方法:

相关回调方法将包含 LibraryParams 对象包含有关客户端应用的内容树类型的额外信号 感兴趣的主题。