如何在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并维护广告播放状态:
- 预定义广告媒体项(区分可跳过/不可跳过)
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()
- 配置本地广告插入组件
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
相关产品推荐
相关产品推荐

