若要将 IMA SDK for Android 集成到您的应用中,最快速、最直接的方法就是让 SDK 处理所有的广告播放逻辑,而让您的应用专注于播放内容视频。此方法称为“SDK 自有广告播放”,是使用入门中的默认选项。
不过,如果您还想在视频播放器中播放广告,SDK 就提供了一个接口。我们将这种方法称为“自定义广告播放”,本指南的其余部分将介绍其实现方法。
前提条件
- 基本的 IMA 集成。
如果您目前没有基本的 IMA 集成,我们建议您先查看 GitHub 上的高级示例。此示例已经实现了自定义广告播放。本指南的其余部分将介绍使用 IMA 广告播放自定义广告所需的功能。
VideoAdPlayer 界面
自定义广告播放要求您的应用实现 VideoAdPlayer
接口。SDK 使用此接口通知您的应用播放广告视频。您的应用还使用此接口将主要视频广告事件告知 SDK。请按照以下步骤实现该接口。
创建 VideoAdPlayer
第一步是在 requestAds()
中创建匿名 VideoAdPlayer
类:
private VideoAdPlayer videoAdPlayer;
...
private void requestAds(String adTagUrl) {
videoAdPlayer = new VideoAdPlayer() {
};
}
添加视频方法
接下来,添加指示视频播放器播放、加载、停止和暂停广告视频的方法。我们还在此处添加了释放播放器并获取音量的方法:
videoAdPlayer = new VideoAdPlayer() {
@Override
public void playAd() {
if (mIsAdDisplayed) {
videoPlayer.resume();
} else {
isAdDisplayed = true;
videoPlayer.play();
}
}
@Override
public void loadAd(String url) {
isAdDisplayed = true;
videoPlayer.setVideoPath(url);
}
@Override
public void stopAd() {
videoPlayer.stopPlayback();
}
@Override
public void pauseAd() {
videoPlayer.pause();
}
@Override
public void release() {
// any clean up that needs to be done
}
@Override
public int getVolume() {
return videoPlayer.getVolume();
}
};
这些方法是视频播放器自身的类似方法的精简封装容器。请注意,这些方法会设置一个内部变量,用于跟踪广告是否展示。在自定义广告播放过程中,视频播放器会同时播放内容视频广告和视频广告,因此您需要跟踪当前展示的视频广告。
广告播放进度
VideoAdPlayer
接口会实现另一个接口,即 AdProgressProvider
,因此您还必须实现该接口。它只有一种方法(即 getAdProgress()
),SDK 使用该方法获取广告的播放信息。将其添加到您的匿名 VideoAdPlayer
类中(在其他方法下方):
VideoAdPlayer videoAdPlayer = new VideoAdPlayer() {
...
@Override
public VideoProgressUpdate getAdProgress() {
if (!isAdDisplayed || videoPlayer.getDuration() <= 0) {
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
}
return new VideoProgressUpdate(videoPlayer.getCurrentPosition(),
videoPlayer.getDuration());
}
};
getAdProgress()
会返回 VideoProgressUpdate
类型,该类型必须包含视频的当前位置和时长。如果播放器未播放广告,或者无法播放时长,则让其返回 VideoProgressUpdate.VIDEO_TIME_NOT_READY
,如示例中所示。
管理视频回调
自定义广告播放要求您的应用向 SDK 通知重大视频事件。从 SDK 的视图中,这些是由 VideoAdPlayer.VideoAdPlayerCallback
接口描述的回调。在调用回调方法本身之前,您需要能够应 SDK 的请求添加和移除回调。这是在 VideoAdPlayer
内使用 addCallback()
和 removeCallback()
完成的:
private List<VideoAdPlayerCallback> adCallbacks = new ArrayList<>(1);
VideoAdPlayer videoAdPlayer = new VideoAdPlayer() {
...
@Override
public void addCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
adCallbacks.add(videoAdPlayerCallback);
}
@Override
public void removeCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
adCallbacks.remove(videoAdPlayerCallback);
}
};
此实现对要对其调用 List<>.add()
和 remove()
方法的回调使用 List<>
。
调用回调
现在,SDK 已经能够指示应用添加和移除回调,接下来可以定义在哪些位置调用回调。当发生重大视频事件(例如播放、暂停或恢复视频,或者视频播放完毕或遇到错误)时,您的应用需要调用这些回调。
为此,请展开 SampleVideoPlayer
以添加从 VideoFragment
添加的这些视频事件的监听器。之所以在 SampleVideoPlayer
中创建一个单独的监听器来调用这些广告回调,是因为 SampleVideoPlayer
并不了解广告的任何信息,因此您必须将其视频事件转发给可以处理广告的内容。
public interface OnVideoEventsListener {
void onPlay();
void onResume();
void onPause();
void onError();
}
private final List<OnVideoEventsListener> onVideoEventsListeners = new ArrayList<>(1);
public void addVideoEventsListener(OnVideoEventsListener listener) {
onVideoEventsListeners.add(listener);
}
启动、暂停和继续
创建一个新的枚举以跟踪播放状态,并为 SampleVideoPlayer
中的 start()
和 pause()
方法添加新的替换项:
private enum PlaybackState {
STOPPED, PAUSED, PLAYING
}
private PlaybackState playbackState = PlaybackState.STOPPED;
@Override
public void start() {
super.start();
switch (playbackState) {
case STOPPED:
for (OnVideoEventsListener listener : onVideoEventsListeners) {
listener.onPlay();
}
break;
case PAUSED:
for (OnVideoEventsListener listener : onVideoEventsListeners) {
listener.onResume();
}
break;
default:
// Already playing; do nothing.
break;
}
playbackState = PlaybackState.PLAYING;
}
@Override
public void pause() {
super.pause();
playbackState = PlaybackState.PAUSED;
for (OnVideoEventsListener listener : onVideoEventsListeners) {
listener.onPause();
}
}
处理错误
替换您在 init()
中设置的视频播放器匿名错误监听器:
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
playbackState = PlaybackState.STOPPED;
for (OnVideoEventsListener listener : onVideoEventsListeners) {
listener.onError();
}
// Returning true signals to MediaPlayer that the error was handled.
// This prevents the completion handler from being called.
return true;
}
实现监听器
返回 VideoFragment
,将匿名 OnVideoEventsListener
添加到您的 SampleVideoPlayer
实例:
mVideoPlayer.addVideoEventsListener(new OnVideoEventsListener() {
@Override
public void onPlay() {
if (isAdDisplayed) {
for (VideoAdPlayerCallback callback : adCallbacks) {
callback.onPlay();
}
}
}
@Override
public void onResume() {
if (isAdDisplayed) {
for (VideoAdPlayerCallback callback : adCallbacks) {
callback.onResume();
}
}
}
@Override
public void onPause() {
if (isAdDisplayed) {
for (VideoAdPlayerCallback callback : adCallbacks) {
callback.onPause();
}
}
}
@Override
public void onError() {
if (isAdDisplayed) {
for (VideoAdPlayerCallback callback : adCallbacks) {
callback.onError();
}
}
}
});
更改 OnVideoCompletedListener
的 onVideoCompleted()
方法,以处理广告视频完整播放的情况:
public void onVideoCompleted() {
// Handle completed event for playing post-rolls.
if (isAdDisplayed) {
for (VideoAdPlayerCallback callback : adCallbacks) {
callback.onEnded();
}
} else {
if (adsLoader != null) {
adsLoader.contentComplete();
}
}
在内容和广告之间切换
此示例使用视频播放器的同一实例来播放内容和广告,因此您需要添加一些逻辑,以在播放器中的内容和广告之间切换。然后,您可以重新加载并跳转至内容视频以返回到广告开始播放的位置。为此,请添加两个函数:
private int savedContentPosition = 0;
private void pauseContent() {
savedContentPosition = videoPlayer.getCurrentPosition();
videoPlayer.stopPlayback();
isAdDisplayed = true;
}
private void resumeContent() {
videoPlayer.setVideoPath(getString(R.string.content_url));
videoPlayer.seekTo(mSavedContentPosition);
videoPlayer.play();
isAdDisplayed = false;
}
当在 VideoFragment.onAdEvent()
中收到 CONTENT_PAUSE_REQUESTED
和 CONTENT_RESUME_REQUESTED
事件时,系统会分别调用这两个事件:
case CONTENT_PAUSE_REQUESTED:
pauseContent();
break;
case CONTENT_RESUME_REQUESTED:
resumeContent();
break;
启用自定义广告播放
最后一步是告知 SDK 您正在使用自定义广告播放。
通过将 VideoAdPlayer
传递给 AdDisplayContainer
来完成此操作:
adDisplayContainer.setPlayer(videoAdPlayer);
您需要将播放器传递给 setPlayer()
。否则,SDK 会使用 SDK 拥有的播放机制。
大功告成。以上是向您的 IMA 实现中添加自定义广告播放所需的所有步骤。如果您遇到问题,可以将您自己的实现与 GitHub 上的高级示例进行比较。