本指南适用于为了参与 Google 中介实时出价 (RTB) 而构建出价适配器的广告联盟。如果您是发布商,请参阅发布商中介说明。
出价适配器是集成的客户端部分。通过该适配器,您的广告联盟 SDK 可与 Google 移动广告 SDK 通信,以加载您的出价工具投放的广告。
要让出价正常工作,您的适配器需要处理各种操作,包括初始化、收集信号、加载广告以及传达广告生命周期事件。在本指南中,我们将逐步介绍应如何实现适配器来处理这些操作。
出价适配器的工作流程
初始化
下面详细地展示了适配器的整个请求、响应和呈现生命周期流程:
适配器负责完成该工作流程的以下部分:
第 4-7 步:初始化适配器,在初始化完成后回调 Google 移动广告 SDK。
第 10-13 步:收集来自您的广告联盟 SDK 的信号,以将其发送至您的出价工具供参与 RTB 请求使用,并将其转发至 Google 移动广告 SDK。
第 18-21 步:如果您的出价工具返回胜出的出价,则根据出价工具中的响应加载广告。广告加载后,向 Google 移动广告 SDK 发出已加载该广告的通知。
第 23 步及后续步骤:在您的广告展示时,向 Google 移动广告 SDK 通知如下事件:展示事件、点击事件以及广告展示生命周期中发生的其他广告事件。
实现出价适配器
如需为 Google 移动广告 SDK 创建出价适配器,您必须扩展 RtbAdapter
抽象类。以下部分介绍了 RtbAdapter
中的各个抽象方法。
getSDKVersionInfo()
此方法应会返回您的 SDK 版本。此版本信息会作为 OpenRTB 请求的一部分传递给出价工具。
此方法要求您返回 VersionInfo
。以下示例显示了如何将 SDK 的字符串版本转换为 VersionInfo.
@Override
public VersionInfo getSDKVersionInfo() {
// Get your SDK's version as a string. E.g. "1.2.3"
// String versionString = YourSdk.getVersion();
String splits[] = versionString.split("\\.");
if (splits.length >= 3) {
int major = Integer.parseInt(splits[0]);
int minor = Integer.parseInt(splits[1]);
int micro = Integer.parseInt(splits[2]);
return new VersionInfo(major, minor, micro);
}
String logMessage = String.format("Unexpected SDK version format: %s." +
"Returning 0.0.0 for SDK version.", sdkVersion);
Log.w(TAG, logMessage);
return new VersionInfo(0, 0, 0);
}
getVersionInfo()
此方法应会返回您的适配器版本。此版本信息会作为 OpenRTB 请求的一部分传递给出价工具。
Google 标明版本的开源适配器使用 4 位数的适配器版本模式,但 VersionInfo
只允许有 3 位数。为了解决此问题,建议将最后两位数字组合为补丁版本,如下所示。
@Override
public VersionInfo getVersionInfo() {
// Get your adapters's version as a string. E.g. "1.2.3.0"
String versionString = BuildConfig.VERSION_NAME;
String splits[] = versionString.split("\\.");
if (splits.length >= 4) {
int major = Integer.parseInt(splits[0]);
int minor = Integer.parseInt(splits[1]);
int micro = Integer.parseInt(splits[2]) * 100 + Integer.parseInt(splits[3]);
return new VersionInfo(major, minor, micro);
}
String logMessage = String.format("Unexpected adapter version format: %s." +
"Returning 0.0.0 for adapter version.", versionString);
Log.w(TAG, logMessage);
return new VersionInfo(0, 0, 0);
}
initialize()
超时:30 秒
在适配器中,initialize()
方法是第一个被调用的方法。每次会话仅调用此方法一次。此方法会为您提供 MediationConfiguration
对象列表,这些对象表示此应用中为您的广告联盟配置的展示位置的完整列表;您可以循环遍历此列表,以解析每个展示位置的凭据,并将相关数据传递到您的 SDK 进行初始化。
您的 SDK 经过初始化且可以开始接收广告请求后,请调用 InitializationCompleteCallback
的 onInitializationSucceeded()
方法。系统会将此回调函数转发给应用发布商,让他们知道自己可以开始加载广告了。
@Override
public void initialize(Context context,
InitializationCompleteCallback initializationCompleteCallback,
List<MediationConfiguration> mediationConfigurations) {
// Initialize your ad network's SDK.
...
// Invoke the InitializationCompleteCallback once initialization completes.
initializationCompleteCallback.onInitializationSucceeded();
}
collectSignals()
超时:1 秒
每次发布商请求广告时,系统都会创建一个新的 RtbAdapter
实例并调用 collectSignals()
方法。在该广告的整个广告请求、响应和呈现生命周期内,都将使用 RtbAdapter
的这个实例。通过 collectSignals()
方法,您的适配器能够提供来自设备的信号,以便作为 OpenRTB 请求的一部分发送至您的出价工具。
collectSignals()
在后台线程上调用。Google 移动广告 SDK 会同时要求所有参与出价的适配器提供信号。请遵循这一要求,在此期间限制对界面线程的调用。凡是您的适配器或 SDK 为收集信号所需完成的繁重工作,均应在 initialize()
方法中完成并进行缓存。
在您准备好信号后,请使用编码的信号调用 onSuccess()
回调函数。
下面是一个实现示例:
@Override
public void collectSignals(RtbSignalData rtbSignalData,
SignalCallbacks signalCallbacks) {
String signals = YourSdk.getSignals();
signalCallbacks.onSuccess(signals);
}
如果您的适配器收集信号失败,请调用 signalCallbacks.onFailure()
并提供解释错误情况的字符串。
实现广告加载方法
超时:10 秒
如果您的出价工具返回胜出的出价,则 Google 移动广告 SDK 会调用您的适配器以加载胜出的广告,这样便可从出价工具返回的数据中将您的 SDK 加载该广告所需的全部数据都传递给您。
具体调用的加载方法取决于此请求所针对的广告格式:
广告格式 | 加载方法 |
---|---|
横幅广告 | loadBannerAd()
|
插页式广告 | loadInterstitialAd()
|
激励广告 | loadRewardedAd()
|
为适配器支持的广告格式实现这些方法。
加载方法可以在界面线程上调用,也可以在您提供信号的同一适配器实例上调用。此方法会为您提供以下参数:
MediationAdConfiguration
- 包含您的 SDK 加载出价胜出的广告所需的参数,例如出价响应和发布商在 AdMob 界面中配置的所有凭据。MediationAdLoadCallback
对象 - 用于在加载成功或失败时通知 Google 移动广告 SDK。
您的 SDK 加载广告后,请调用 mediationAdLoadCallback.onSuccess()
。如果广告加载失败,请调用 mediationAdLoadCallback.onFailure()
并提供解释错误情况的字符串。
mediationAdLoadCallback.onSuccess()
方法要求您传入一个对象,向 Google 移动广告 SDK 定义的“广告”接口之一进行确认。这些广告接口会要求您提供有关广告的一些信息。
MediationAdConfiguration
还具有 getWatermark()
方法,用于返回表示 PNG 图片的采用 Base64 编码的字符串。此图片应平铺在您广告上的透明叠加层中。如需获得有关如何呈现水印的更多指导,请与 Google 联系。该方法包含有关所展示广告的元数据,供发布商用来确定展示的广告的来源。
对于横幅广告,系统会要求您提供横幅广告视图。对于插页式广告和激励广告,系统会要求您实现 show()
方法,以便稍后展示广告。我们建议您最好让执行广告加载的类同时负责实现这些广告方法。
以下是 loadBannerAd()
的示例实现代码。请注意,适配器的实现代码看起来会有所不同,因为适配器集成所针对的 SDK 也不同。
public final class SampleRtbAdapter extends RtbAdapter {
...
@Override
public void loadBannerAd(
MediationBannerAdConfiguration adConfiguration,
MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> callback) {
SampleBannerRenderer bannerRenderer =
new SampleBannerRenderer(adConfiguration, callback);
bannerRenderer.render();
}
}
// Renders a banner ad, and forwards callbacks to the Google Mobile Ads SDK.
public class SampleBannerRenderer implements MediationBannerAd {
private MediationBannerAdConfiguration adConfiguration;
private final MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> adLoadCallback;
private AdView adView;
private MediationBannerAdCallback callback;
public SampleRtbBannerRenderer(
MediationBannerAdConfiguration adConfiguration,
MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> adLoadCallback) {
this.adConfiguration = adConfiguration;
this.adLoadCallback = adLoadCallback;
}
public void render() {
adView = new AdView(adConfiguration.getContext());
adView.setAdSize(adConfiguration.getAdSize());
// serverParameters are the parameters entered in the AdMob UI for your network.
adView.setAdUnitId(adConfiguration.getServerParameters().getString("adUnitId"));
// Map the callbacks from your SDK to Google's SDK.
adView.setAdListener(new AdListener() {
// See the next step for more information on callback mapping.
// ...
});
// Get the bid response and watermark from the ad configuration and
// pass the relevant information to your SDK.
String ad = adConfiguration.getBidResponse();
String watermark = adConfiguration.getWatermark();
Bundle extras = new Bundle();
extras.putString("bid", ad);
extras.putString("watermark", watermark);
AdRequest request = new AdRequest.Builder()
.addNetworkExtrasBundle(AdMobAdapter.class, extras)
.build();
adView.loadAd(request);
}
// MediationBannerAd implementation
@NonNull
@Override
public View getView() {
return adView;
}
}
传达广告展示生命周期事件
适配器负责的最后一个操作是将所有展示生命周期事件通知 Google 移动广告 SDK,以便将它们转发给发布商。无论是哪个广告联盟投放广告,发布商都会希望在特定时间执行这些回调函数,因此非常重要的一点是在适当时间尽可能多地调用这些回调函数,以便 Google 移动广告 SDK 将它们转发给发布商。
适配器应在适用情况下调用以下事件:
适用于所有格式 | |
---|---|
方法 | 调用时间 |
reportAdClicked()
|
用户点击了广告时。 |
reportAdImpression()
|
广告呈现了展示时。 |
onAdOpened()
|
广告展示了全屏视图时。 |
onAdClosed()
|
广告的全屏视图已关闭时。 |
onAdLeftApplication()
|
广告导致用户离开应用时。 |
激励广告 | |
onRewarded()
|
用户获得奖励时。 |
视频回调(激励广告和原生广告) | |
onVideoStarted()
|
广告视频开始时。 |
onVideoCompleted()
|
广告视频播完时。 |
适配器在调用 mediationAdLoadCallback.onSuccess()
时会返回 MediationAdLoadCallback<MediationAdT, MediationAdCallbackT>
对象。适配器需要保留此对象,并使用它调用您的广告上发生的展示事件。
通常,这类事件大都由您广告联盟的 SDK 提供支持。适配器的作用只是将回调函数从您的广告联盟 SDK 映射到 Google 移动广告 SDK。
以下示例演示了如何将回调函数从您 SDK 的广告监听器转发至 Google 移动广告 SDK:
adView.setAdListener(new AdListener() {
public void onAdLoaded() {
callback = adLoadCallback.onSuccess(SampleBannerRenderer.this);
}
public void onAdImpression() {
if (callback != null) {
callback.reportAdImpression();
}
}
public void onAdFailedToLoad(LoadAdError adError) {
adLoadCallback.onFailure("Error: " + adError.toString());
}
public void onAdClosed() {
if (callback != null) {
callback.onAdClosed();
}
}
public void onAdOpened() {
if (callback != null) {
callback.onAdOpened();
callback.reportAdClicked();
}
}
public void onAdLeftApplication() {
if (callback != null) {
callback.onAdLeftApplication();
}
}
});
原生广告展示跟踪所需的素材资源
当原生广告的 1 像素可见时,Google 移动广告 SDK 会记录一次广告展示。如果您的广告联盟 SDK 要求显示特定素材资源才能呈现有效的展示,您的出价方可以在出价响应中指明这些必需的原生素材资源。然后,Google 移动广告 SDK 会在记录展示次数之前验证所需的原生广告素材资源是否已展示。
如需详细了解如何在出价响应中指定其他必需的素材资源,请参阅原生广告必需的素材资源文档。
显示广告错误
对于全屏广告格式(例如插页式广告和激励广告),您需要在成功加载回调中提供 MediationInterstitialAd
或 MediationRewardedAd
的实现,以便 Google 移动广告 SDK 可以请求您的适配器展示广告。
Google 移动广告 SDK 希望,如果适配器成功加载了广告,那么在发布商要求展示广告时,广告就应该可以展示。这意味着,每次展示调用都应产生一次展示。
不过,在某些极端情况下,您可能无法展示广告。如果您无法展示广告,请调用 onAdFailedToShow()
回调以取消展示。
下表显示了呈现回调如何影响全屏广告格式的展示次数记录:
回拨 | 结果 |
---|---|
onAdOpened() | Impression recorded
|
onAdFailedToShow() | Impression failure1
|
几秒钟内显示“以上都不是” | Impression recorded
|
1 对于未能完成的展示,广告联盟不会为其收取费用,但会影响可结算事件费率调整。如需了解详情,请参阅出价请求信号。
以下模拟示例演示了广告展示调用可能会导致失败的加载/展示生命周期。
final class SampleRtbAdapter extends RtbAdapter implements MediationRewardedAd {
private MediationRewardedAdCallback callback;
private RewardedAd rewardedAd;
...
@Override
public void loadRewardedAd(
MediationRewardedAdConfiguration adConfiguration,
final MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback> loadCallback) {
// Load an ad. This mock example uses Google's SDK, but in practice
// your adapter will load the ad using your ad network's SDK.
RewardedAd.load(adConfiguration.getContext(),
"ca-app-pub-3940256099942544/5224354917",
new AdRequest.Builder().build(),
new RewardedAdLoadCallback() {
@Override
public void onAdLoaded(@NonNull RewardedAd rewardedAd) {
// When the ad loads, invoke the load success callback.
callback = loadCallback.onSuccess(SampleRtbAdapter.this);
}
});
}
@Override
public void showAd(Context context) {
// In this mock example, your ad network requires an activity context, but
// didn't receive one, making you unable to show the ad.
if (!(context instanceof Activity)) {
AdError error = new AdError(1, "Context must be an activity",
"com.google.ads.mediation.sample");
callback.onAdFailedToShow(error);
}
// This example shows Google SDK's callbacks, but it's likely your SDK
// has similar presentation callbacks.
rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() {
@Override
public void onAdShowedFullScreenContent() {
// Your ad network SDK successfully showed the ad. Call onAdOpened().
callback.onAdOpened();
}
@Override
public void onAdFailedToShowFullScreenContent(AdError adError) {
// Your ad network SDK failed to show the ad, invoke onAdFailedToShow.
// In practice, you will map your SDK's error to an AdError.
AdError error = new AdError(adError.getCode(), adError.getMessage(),
adError.getDomain());
callback.onAdFailedToShow(adError);
}
});
rewardedAd.show((Activity) context, ...);
}
}