广告网络中介适配器开发

本指南适用于要构建中介适配器的广告联盟。如果您是发布商,请参阅发布商中介说明

中介适配器是广告联盟与 Google 移动广告中介之间的通信层。适配器会投放来自您的广告网络的广告,同时将相关广告事件转发给 AdMob。适配器负责实现 GADMAdNetworkAdapter 协议,以确保其提供所需的功能。

前提条件

  • Xcode 15.3 或更高版本
  • 部署目标为 8.0 或更高版本
  • 下载 Google 移动广告 iOS SDK 的“Mediation Adapters”文件夹中的其他标头

要创建中介适配器,您的广告网络必须与 Google 建立良好的合作关系。

示例广告联盟

本指南演示了如何为示例广告联盟构建适配器。 示例广告联盟 SDK 包含大多数广告联盟都会提供的一些典型的类:

@interface SampleBanner : UILabel

@property(nonatomic, copy) NSString *adUnit;
@property(nonatomic, weak) id<SampleBannerAdDelegate> delegate;
- (void)fetchAd:(SampleAdRequest *)request;

@end

typedef NS_ENUM(NSInteger, SampleErrorCode) {
  SampleErrorCodeBadRequest = 0,
  SampleErrorCodeUnknown = 1,
  SampleErrorCodeNetworkError = 2,
  SampleErrorCodeNoInventory = 3,
};

@interface SampleAdRequest : NSObject

@property(nonatomic, assign) BOOL testMode;
@property(nonatomic, copy) NSArray *keywords;

@end

@interface SampleInterstitial : NSObject

@property(nonatomic, copy) NSString *adUnit;
@property(nonatomic, weak) id<SampleInterstitialAdDelegate> delegate;
@property(nonatomic, assign, getter=isInterstitialLoaded) BOOL interstitialLoaded;
- (void)fetchAd:(SampleAdRequest *)request;
- (void)show;

@end

@protocol SampleBannerAdDelegate
- (void)bannerDidLoad:(SampleBanner *)banner;
- (void)banner:(SampleBanner *)banner didFailToLoadAdWithError:(SampleErrorCode)error;
- (void)bannerWillLeaveApplication:(SampleBanner *)banner;

@end

@protocol SampleInterstitialAdDelegate<NSObject>
- (void)interstitialDidLoad:(SampleInterstitial *)interstitial;
- (void)interstitial:(SampleInterstitial *)interstitial
    didFailToLoadAdWithError:(SampleErrorCode)error;
- (void)interstitialWillPresentScreen:(SampleInterstitial *)interstitial;
- (void)interstitialWillDismissScreen:(SampleInterstitial *)interstitial;
- (void)interstitialDidDismissScreen:(SampleInterstitial *)interstitial;
- (void)interstitialWillLeaveApplication:(SampleInterstitial *)interstitial;

@end

如需详细了解这些类,请参阅完整的示例 SDK 实现

连接器

中介适配器使用实现 GADMAdNetworkConnector 协议的对象与 Google 移动广告 SDK 进行交互。从现在开始,我们将这个对象称为连接器。连接器会为广告请求提供必要的信息,并提供一种回调广告事件和用户互动的方法,向中介回调。连接器会在初始化时提供给中介适配器。设置适配器,以将连接器存储在实例变量中:

- (id)initWithGADMAdNetworkConnector:(id<GADMAdNetworkConnector>)c {
  if ((self = [super init])) {
    _connector = c;
  }
  return self;
}

服务器参数

您的广告网络可能需要使用标识符来识别发布商。例如,示例广告联盟需要使用广告单元。这些必需的服务器参数通过 credentials 方法提供给适配器,该方法会返回标识符和相应值的 NSDictionary。下面这行代码会从连接器中为示例广告联盟检索名为 ad_unit 的服务器参数:

self.interstitialAd.adUnit = [[self.connector credentials] objectForKey:@"ad_unit"];

中介广告联盟设置

广告联盟必须将自身所需的服务器参数告知 AdMob。这样,AdMob 就可以在前端配置您的广告网络。

在前端为中介启用广告联盟时,AdMob 会要求您输入实例化适配器所需的值。下面的屏幕截图显示 Millennial Media 需要应用展示位置标识符 (APID),而 InMobi 需要应用 ID。

如需详细了解如何配置中介广告联盟,请参阅这篇文章

发出中介广告请求

对于中介横幅广告请求,系统会在适配器实例化后立即调用 getBannerWithSize 方法。此方法不会返回任何值。它会指示适配器开始通过广告联盟异步提取广告。让适配器监听 SDK 的回调。如果您的 SDK 不支持给定的广告尺寸或不支持横幅广告,请在连接器上调用 adapter:didFailAd: 方法。

示例广告联盟的 getBannerWithSize 实现如下所示:

- (void)getBannerWithSize:(GADAdSize)adSize {
  //The adapter should fail immediately if the adSize is not supported
  if (!GADAdSizeEqualToSize(adSize, GADAdSizeBanner) &&
      !GADAdSizeEqualToSize(adSize, GADAdSizeMediumRectangle) &&
      !GADAdSizeEqualToSize(adSize, GADAdSizeFullBanner) &&
      !GADAdSizeEqualToSize(adSize, GADAdSizeLeaderboard)) {
    NSString *errorDesc =
        [NSString stringWithFormat:@"Invalid ad type %@, not going to get ad.",
                                   NSStringFromGADAdSize(adSize)];
    NSDictionary *errorInfo = [NSDictionary
        dictionaryWithObjectsAndKeys:errorDesc, NSLocalizedDescriptionKey, nil];
    NSError *error = [NSError errorWithDomain:GADErrorDomain
                                         code:GADErrorMediationInvalidAdSize
                                     userInfo:errorInfo];
    [self.connector adapter:self didFailAd:error];
    return;
  }
  self.bannerAd = [[SampleBanner alloc]
      initWithFrame:CGRectMake(0, 0, adSize.size.width, adSize.size.height)];

  self.bannerAd.delegate = self;
  self.bannerAd.adUnit = [[self.connector credentials] objectForKey:@"ad_unit"];

  // Setup request parameters.
  SampleAdRequest *request = [[SampleAdRequest alloc] init];
  request.testMode = self.connector.testMode;
  request.keywords = self.connector.userKeywords;
  [self.bannerAd fetchAd:request];
  NSLog(@"Requesting banner from Sample Ad Network");
}

其他定位参数

连接器包含一些常用的定位信息,供您在广告定位时使用:

  • userKeywords
  • testMode
  • childDirectedTreatment

中介额外信息 (extras)

通过使用中介额外参数,可让广告联盟支持连接器提供的其他请求参数未涵盖的额外定位参数或输入。此功能可通过实现 GADAdNetworkExtras 协议的类提供。下面展示了 SampleAdNetworkExtras 类的示例:

@interface SampleAdNetworkExtras : NSObject <GADAdNetworkExtras>
/// Should ad volume audio be muted.
@property(nonatomic, assign) BOOL muteAudio;

@end

发布商必须在发出广告请求时传递 GADAdNetworkExtras 子类的实例。您可以通过连接器访问 GADAdNetworkExtras 子类的这个实例。下文介绍了在为广告联盟构建广告请求时如何使用中介额外信息 (extras):

SampleAdRequest *request = [[SampleAdRequest alloc] init];
SampleAdNetworkExtras *myAdNetworkExtras = [self.connector networkExtras];
request.muteAudio = myAdNetworkExtras.muteAudio;
[self.bannerAd fetchAd:request];

为确保可通过连接器访问 GADAdNetworkExtras 子类,请实现 networkExtrasClass 并返回 GADAdNetworkExtras 子类:

+ (Class<GADAdNetworkExtras>)networkExtrasClass {
  return [SampleAdNetworkExtras class];
}

如果您的适配器不使用中介额外信息 (extras),networkExtrasClass 方法会返回 Nil

通知中介

让适配器为广告联盟实现广告监听器,并通过连接器转发相关的广告事件回调函数。下表说明了在什么情况下调用每种委托方法:

方法 致电时间
adapter:didReceiveAdView: 横幅广告请求成功。
adapter:didFailAd: 横幅广告请求失败。
adapterDidGetAdClick: 用户点击了横幅广告。
adapterWillPresentFullScreenModal: 横幅广告将展示一个应用内叠加层。
adapterWillDismissFullScreenModal: 应用内叠加层将关闭。
adapterDidDismissFullScreenModal: 应用内叠加层已关闭。
adapterWillLeaveApplication: 用户点击了会启动另一个应用的广告,因此应用将被置于后台或终止。

以下示例实现了示例广告联盟中的 SampleBannerAdDelegate 接口,以转发错误和状态消息:

- (void)bannerDidLoad:(SampleBanner *)banner {
  [self.connector adapter:self didReceiveAdView:banner];
}

- (void)banner:(SampleBanner *)banner didFailWithError:(SampleErrorCode)error {
  [self.connector adapter:self
                didFailAd:[NSError errorWithDomain:@"Ad request failed"
                                              code:error
                                          userInfo:nil]];
}

- (void)bannerWillLeaveApplication:(SampleBanner *)banner {
  [self.connector adapterDidGetAdClick:self];
  [self.connector adapterWillLeaveApplication:self];
}

启动应用内叠加层与启动外部应用的对比

点击横幅广告时,该广告可能会打开一个全屏叠加层,也可能会打开外部应用(如 Safari 或应用商店)。在这两种情况下调用的连接器回调函数是不同的。

如果点击横幅广告时打开的是全屏叠加层,则在显示叠加层时调用 adapterWillPresentFullScreenModal:。当叠加层关闭时,请同时调用 adapterWillDismissFullScreenModal:adapterDidDismissFullScreenModal:

如果点击横幅广告(或点击全屏叠加层)时导致用户离开应用,请调用 adapterWillLeaveApplication: 回调函数。

现在,您便拥有了一个适用于横幅广告的有效中介适配器。我们在 GitHub 上提供了 SampleAdapter 的完整实现,以供参考。

实现插页式广告适配器

插页式广告的适配器实现与横幅广告的类似。系统会在适配器实例化后立即调用 getInterstitial 方法。此方法不会返回任何内容。适配器预计会开始通过广告联盟异步提取广告。让适配器充当 SDK 的代理,以监听回调。如果您的 SDK 不支持插页式广告,请按如下方式调用连接器的 adapter:didFailAd: 方法:

- (void)getInterstitial {
  self.interstitialAd = [[SampleInterstitial alloc] init];
  self.interstitialAd.delegate = self;
  self.interstitialAd.adUnit =
      [[self.connector credentials] objectForKey:@"ad_unit"];

  SampleAdRequest *request = [[SampleAdRequest alloc] init];
  // Setup request parameters.
  request.testMode = self.connector.testMode;
  request.keywords = self.connector.userKeywords;

  [self.interstitialAd fetchAd:request];
  NSLog(@"Requesting interstitial from Sample Ad Network");
}

MediationBannerAdapter 一样,您可以从连接器接收发布商身份识别信息和广告定位信息。

通知中介

让适配器为广告联盟实现广告代理,并通过连接器转发相关的广告事件回调函数。下表说明了各个委托方法的调用时机:

方法 致电时间
adapterDidReceiveInterstitial: 插页式广告请求成功时。
adapter:didFailInterstitial: 插页式广告请求失败。
adapterDidGetAdClick: 用户点击了插页式广告。
adapterWillPresentInterstitial: 插页式广告即将展示。
adapterWillDismissInterstitial: 插页式广告即将关闭。
adapterDidDismissInterstitial: 插页式广告已关闭。
adapterWillLeaveApplication: 应用将转变为后台运行或终止,因为用户点击了将启动另一个应用的广告。

以下示例实现了示例广告联盟的 SampleInterstitialAdDelegate 接口,以转发错误和状态消息:

- (void)interstitialDidLoad:(SampleInterstitial *)interstitial {
  [self.connector adapterDidReceiveInterstitial:self];
}

- (void)interstitial:(SampleInterstitial *)interstitial
    didFailWithError:(SampleErrorCode)error {
  [self.connector adapter:self
      didFailInterstitial:[NSError errorWithDomain:@"Ad request failed"
                                              code:error
                                          userInfo:nil]];
}

- (void)interstitialWillPresentScreen:(SampleInterstitial *)interstitial {
  [self.connector adapterWillPresentInterstitial:self];
}

- (void)interstitialWillDismissScreen:(SampleInterstitial *)interstitial {
  [self.connector adapterWillDismissInterstitial:self];
}

- (void)interstitialDidDismissScreen:(SampleInterstitial *)interstitial {
  [self.connector adapterDidDismissInterstitial:self];
}

展示插页式广告

在适配器调用 adapterDidReceiveInterstitial 后,让其等待系统调用 presentInterstitialFromRootViewController 之后再展示插页式广告。插页式广告何时展示,可能需要在接收后的几分钟内由应用开发者决定。

下面是示例广告联盟的 presentInterstitialFromRootViewController: 实现:

- (void)presentInterstitialFromRootViewController:
    (UIViewController *)rootViewController {
  if ([self.interstitialAd isLoaded]) {
    [self.interstitialAd show];
  }
}

您的中介适配器已准备好处理插页式广告了。我们在 GitHub 上提供了 SampleAdapter 的完整实现,以供参考。

常见问题解答

我应将适配器纳入 SDK 库还是将其作为单独的库?
我们建议将适配器纳入 SDK 库,这样开发者只需引用一个库,即可整合您的广告网络。
如果我的适配器仅支持横幅广告,我该怎么办?

如果您的适配器仅支持横幅广告,请让其将插页式广告请求的错误事件转发给中介:

- (void)getInterstitial {
  NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey : @"Some error" };
  NSError *error = [NSError errorWithDomain:GADErrorDomain
                                       code:GADErrorInvalidRequest
                                   userInfo:errorInfo];
  [self.connector adapter:self didFailInterstitial:error];
  return;
}
如果我的适配器仅支持插页式广告,该怎么办?

如果您的适配器仅支持插页式广告,请将其设置为针对横幅广告请求向中介转发错误事件:

- (void)getBannerWithSize:(GADAdSize)adSize {
  NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey : @"Some error" };
  NSError *error =
      [NSError errorWithDomain:GADErrorDomain
                          code:GADErrorInvalidRequest
                      userInfo:errorInfo];
  [self.connector adapter:self didFailAd:error];
  return;
}