You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ExoPlayer中像Spotify一样集成音频广告(每3个播客插播)

ExoPlayer 本地广告插入实现(适配新版API)

一、服务绑定场景下的ExoPlayer初始化

在服务的onBind方法中完成ExoPlayer初始化,不要提前调用prepare,让播放器保持IDLE状态,仅做基础配置:

// 在Service类中
private lateinit var player: ExoPlayer

override fun onBind(intent: Intent): IBinder? {
    val rendererFactory = DefaultRenderersFactory(this)
    // 初始化空MediaItem,避免播放器自动进入准备状态
    val emptyMediaItem = MediaItem.Builder().build()
    player = ExoPlayer.Builder(this)
        .setRenderersFactory(rendererFactory)
        .build()
    player.setMediaItem(emptyMediaItem)
    return yourCustomBinder // 返回自定义Binder供Activity绑定调用
}

二、配置本地广告插入(替代废弃的setAdsLoaderProvider)

使用setLocalAdInsertionComponents配置广告加载逻辑,核心是自定义AdLoader并维护广告播放状态:

  1. 预定义广告媒体项(区分可跳过/不可跳过)
private var podcastPlayCount = 0
// 可跳过广告:30秒后允许跳过
private val skippableAd = MediaItem.Builder()
    .setUri("your_skippable_ad_uri")
    .setAdConfiguration(AdConfiguration.Builder()
        .setSkipOffsetMs(30000)
        .build())
    .build()
// 不可跳过广告:设置为0表示无法跳过
private val nonSkippableAd = MediaItem.Builder()
    .setUri("your_nonskippable_ad_uri")
    .setAdConfiguration(AdConfiguration.Builder()
        .setSkipOffsetMs(0)
        .build())
    .build()
  1. 配置本地广告插入组件
player.setLocalAdInsertionComponents(
    Executors.newSingleThreadExecutor(),
    object : AdLoader {
        override fun loadAd(adPlaybackState: AdPlaybackState, adGroupIndex: Int) {
            // 根据需求选择广告类型
            val targetAd = if (needSkippableAd()) skippableAd else nonSkippableAd
            // 更新广告播放状态,注入广告媒体项
            val newAdState = adPlaybackState.withAd(
                adGroupIndex,
                0,
                AdMediaItem(targetAd)
            )
            player.updateAdPlaybackState(newAdState)
        }
    }
)

三、实现“每3个播客插1个广告”的触发逻辑

通过播放器的媒体项切换监听,统计已播放播客数量,达到阈值时插入广告:

player.addListener(object : Player.Listener {
    override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
        // 仅统计自动切换的播客(排除广告切换)
        if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO && mediaItem?.adConfiguration == null) {
            podcastPlayCount++
            // 每播放3个播客,插入广告到下一个播放位置
            if (podcastPlayCount % 3 == 0) {
                val currentIndex = player.currentMediaItemIndex
                val newAdState = player.adPlaybackState
                    .withNewAdGroup(currentIndex + 1)
                player.updateAdPlaybackState(newAdState)
            }
        }
    }

    override fun onAdPlaybackStateChanged(adPlaybackState: AdPlaybackState) {
        // 广告播放完成后,可根据需求重置计数(比如循环累计则不重置)
        val lastAdGroup = adPlaybackState.adGroupCount - 1
        if (lastAdGroup >= 0 && adPlaybackState.getAdGroup(lastAdGroup)?.isCompleted == true) {
            // podcastPlayCount = 0 // 可选:重置计数,重新开始累计
        }
    }
})

四、按钮触发播放逻辑

Activity绑定服务后,通过按钮点击设置播客列表并启动播放:

// Activity中按钮点击事件
playPodcastBtn.setOnClickListener {
    val podcastList = listOf(
        MediaItem.fromUri("podcast_uri_1"),
        MediaItem.fromUri("podcast_uri_2"),
        MediaItem.fromUri("podcast_uri_3"),
        // 更多播客媒体项...
    )
    yourBoundService.player.setMediaItems(podcastList)
    yourBoundService.player.prepare()
    yourBoundService.player.play()
}

关键注意事项

  • 广告的可跳过属性完全由AdConfiguration的skipOffsetMs控制,0表示不可跳过
  • 服务中初始化播放器后务必保持IDLE状态,避免提前加载资源
  • 计数逻辑可灵活调整,比如是否在广告播放后重置,或跨会话累计播放量

内容的提问来源于stack exchange,提问作者krupa parekh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:39:34