APK 缩减

尽可能缩减 APK 大小是开发优质 Android 应用的重要方面。对于定位到发展中市场的应用,以及开发 Android 免安装应用时,这一点尤为重要。在这种情况下,最好尽可能缩减 APK 中包含的 ExoPlayer 库的大小。本页面简要介绍了一些有助于实现此目标的简单步骤。

仅使用所需的依赖项

仅依赖于您实际需要的库模块。例如,以下代码将添加对 ExoPlayer、DASH 和界面库模块的依赖项,这可能对仅播放 DASH 内容的应用而言是必需的:

Kotlin

implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
implementation("androidx.media3:media3-ui:1.4.1")

Groovy

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-exoplayer-dash:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"

启用代码和资源缩减

您应该为应用的发布 build 启用代码和资源缩减。ExoPlayer 的结构允许代码缩减,以有效移除未使用的功能。例如,对于播放 DASH 内容的应用,启用代码缩减功能后,ExoPlayer 对 APK 大小的贡献可减少约 40%。

如需了解如何启用代码和资源缩减,请参阅缩减、混淆处理和优化应用

指定您的应用需要哪些渲染程序

默认情况下,系统会使用 DefaultRenderersFactory 创建播放器的渲染程序。DefaultRenderersFactory 依赖于 ExoPlayer 库中提供的所有 Renderer 实现,因此代码缩减功能不会移除其中任何实现。如果您知道自己的应用只需要一部分渲染程序,则可以改为指定自己的 RenderersFactory。例如,仅播放音频的应用可以在实例化 ExoPlayer 实例时定义如下工厂:

Kotlin

val audioOnlyRenderersFactory =
  RenderersFactory {
    handler: Handler,
    videoListener: VideoRendererEventListener,
    audioListener: AudioRendererEventListener,
    textOutput: TextOutput,
    metadataOutput: MetadataOutput,
    ->
    arrayOf<Renderer>(
      MediaCodecAudioRenderer(context, MediaCodecSelector.DEFAULT, handler, audioListener)
    )
}
val player = ExoPlayer.Builder(context, audioOnlyRenderersFactory).build()

Java

RenderersFactory audioOnlyRenderersFactory =
    (handler, videoListener, audioListener, textOutput, metadataOutput) ->
        new Renderer[] {
            new MediaCodecAudioRenderer(
                context, MediaCodecSelector.DEFAULT, handler, audioListener)
        };
ExoPlayer player = new ExoPlayer.Builder(context, audioOnlyRenderersFactory).build();

这样,系统就可以通过代码缩减来移除其他 Renderer 实现。在此特定示例视频中,系统会移除文本和元数据渲染程序(这意味着播放器不会处理或发出任何字幕或插播式元数据,例如 ICY)。

指定您的应用需要哪些提取器

默认情况下,播放器会创建 Extractor 实例,以使用 DefaultExtractorsFactory 播放渐进式媒体。DefaultExtractorsFactory 依赖于 ExoPlayer 库中提供的所有 Extractor 实现,因此代码缩减不会移除其中任何一个。如果您知道自己的应用只需播放少量容器格式,或者根本不播放渐进式媒体,则可以改为指定自己的 ExtractorsFactory。例如,只需要播放 mp4 文件的应用可以提供如下工厂:

Kotlin

val mp4ExtractorFactory = ExtractorsFactory {
  arrayOf<Extractor>(Mp4Extractor(DefaultSubtitleParserFactory()))
}
val player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, mp4ExtractorFactory)).build()

Java

ExtractorsFactory mp4ExtractorFactory =
    () -> new Extractor[] {new Mp4Extractor(new DefaultSubtitleParserFactory())};
ExoPlayer player =
    new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context, mp4ExtractorFactory))
        .build();

这样一来,就可以通过代码缩减来移除其他 Extractor 实现,从而显著缩减大小。

如果您的应用根本不播放渐进式内容,您应将 ExtractorsFactory.EMPTY 传递给 DefaultMediaSourceFactory 构造函数,然后将该 mediaSourceFactory 传递给 ExoPlayer.Builder 构造函数。

Kotlin

val player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, ExtractorsFactory.EMPTY)).build()

Java

ExoPlayer player =
    new ExoPlayer.Builder(
            context, new DefaultMediaSourceFactory(context, ExtractorsFactory.EMPTY))
        .build();

自定义 MediaSource 实例化

如果您的应用使用的是自定义 MediaSource.Factory,并且您希望通过代码剥离来移除 DefaultMediaSourceFactory,则应将 MediaSource.Factory 直接传递给 ExoPlayer.Builder 构造函数。

Kotlin

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

Java

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

如果您的应用直接使用 MediaSource 而非 MediaItem,则应将 MediaSource.Factory.UNSUPPORTED 传递给 ExoPlayer.Builder 构造函数,以确保 DefaultMediaSourceFactoryDefaultExtractorsFactory 可以通过代码缩减被剥离。

Kotlin

val player = ExoPlayer.Builder(context, MediaSource.Factory.UNSUPPORTED).build()
val mediaSource =
  ProgressiveMediaSource.Factory(dataSourceFactory, customExtractorsFactory)
    .createMediaSource(MediaItem.fromUri(uri))

Java

ExoPlayer player = new ExoPlayer.Builder(context, MediaSource.Factory.UNSUPPORTED).build();
ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(dataSourceFactory, customExtractorsFactory)
        .createMediaSource(MediaItem.fromUri(uri));