出价适配器开发

本指南适用于希望构建出价适配器以便参与 Google 中介的实时出价 (RTB) 的广告联盟。如果您是发布商,请参阅发布商中介说明。

出价适配器是集成的客户端部分。通过该适配器,您的广告网络 SDK 可与 Google 移动广告 SDK 通信,以加载您的出价工具投放的广告。

为了让出价功能正常运行,适配器需要处理初始化、收集信号、加载广告以及传达广告生命周期事件。在本指南中,我们将逐步介绍应如何实现适配器来处理这些操作。

出价适配器的工作流程

初始化

下面详细地展示了适配器的整个请求、响应和呈现生命周期流程:

适配器负责工作流程的以下部分:

  • 第 4-7 步:初始化适配器,并在初始化完成后回调 Google 移动广告 SDK。

  • 第 10-13 步:从广告联盟 SDK 收集要发送给出价方参与实时出价请求的信号,并将其转发到 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 经过初始化并准备好接收广告请求后,请调用 InitializationCompleteCallbackonInitializationSucceeded() 方法。系统会将此回调函数转发给应用发布商,让他们知道自己可以开始加载广告了。

@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();
        }
    }
});

跟踪原生广告展示情况所需的素材资源

Google 移动广告 SDK 会在原生广告有 1 像素的可见区域时记录一次展示。如果您的广告联盟 SDK 需要展示特定素材资源才能呈现有效展示,您的出价工具可以在出价响应中指明这些必需的原生素材资源。然后,Google 移动广告 SDK 会在记录展示之前验证您所需的原生素材资源是否得以展示。

如需详细了解如何在出价响应中指定其他必需的素材资源,请参阅原生必需的素材资源文档

显示广告错误

对于全屏广告格式(例如插页式广告和激励广告),您将在成功加载回调中提供 MediationInterstitialAdMediationRewardedAd 的实现,以便 Google 移动广告 SDK 可以请求您的适配器展示广告。

Google 移动广告 SDK 会认为,如果适配器成功加载了广告,当发布商要求展示广告时,该广告便可以展示。这意味着,每次 show 调用都应产生一次展示。

不过,在某些极端情况下,您可能无法展示广告。如果您无法展示广告,请调用 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, ...);
  }
}