开屏广告

本指南面向植入开屏广告的发布商。

开屏广告是一种特殊的广告格式,适合希望通过应用加载页面创收的发布商。用户可以随时关闭开屏广告。开屏广告可以在用户将您的应用切换为在前台运行时展示。

开屏广告会自动在一个较小的区域内展示您的品牌信息,让用户知道他们是在您的应用中。以下是一个开屏广告示例:

概括来讲,植入开屏广告需要执行的步骤如下:

  1. 创建一个管理器类,用于先加载广告,以备需要展示时使用。
  2. 在应用进入前台事件期间展示广告。
  3. 处理展示回调。

前提条件

务必用测试广告进行测试

在构建和测试应用时,请确保使用的是测试广告,而不是实际投放的广告。否则,可能会导致您的账号被中止。

对于开屏广告,加载测试广告最简便的方法就是使用下面的测试专用广告单元 ID:

ca-app-pub-3940256099942544/5575463023

该测试广告单元 ID 已经过专门配置,可确保每个请求返回的都是测试广告。您可以在自己应用的编码、测试和调试过程中随意使用该测试广告单元 ID。只需确保您会在发布应用前用自己的广告单元 ID 替换该测试广告单元 ID 即可。

如需详细了解移动广告 SDK 的测试广告如何运作,请参阅测试广告

实现 Manager 类

您的广告应该要快速展示,因此最好先加载广告,以备需要展示时使用。这样一来,用户进入您的应用后,广告便可以立即展示。实现管理器类,即可在您需要展示广告之前发出广告请求。

创建一个名为 AppOpenAdManager 的新单例类,并在其中添加相关代码,如下所示:

Swift

class AppOpenAdManager: NSObject {
  var appOpenAd: GADAppOpenAd?
  var isLoadingAd = false.
  var isShowingAd = false

  static let shared = AppOpenAdManager()

  private func loadAd() async {
    // TODO: Implement loading an ad.
  }

  func showAdIfAvailable() {
    // TODO: Implement showing an ad.
  }

  private func isAdAvailable() -> Bool {
    // Check if ad exists and can be shown.
    return appOpenAd != nil
  }
}

Objective-C

@interface AppOpenAdManager ()
@property(nonatomic, strong) GADAppOpenAd *appOpenAd;
@property(nonatomic, assign) BOOL isLoadingAd;
@property(nonatomic, assign) BOOL isShowingAd;

@end

@implementation AppOpenAdManager

+ (nonnull AppOpenAdManager *)sharedInstance {
  static AppOpenAdManager *instance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    instance = [[AppOpenAdManager alloc] init];
  });
  return instance;
}

- (void)loadAd {
  // TODO: Implement loading an ad.
}

// Add this method to the .h file.
- (void)showAdIfAvailable {
  // TODO: Implement showing an ad.
}

- (BOOL)isAdAvailable {
  // Check if ad exists and can be shown.
  return self.appOpenAd != nil;
}

@end

加载广告

下一步是填充 loadAd() 方法。

Swift

private func loadAd() async {
  // Do not load ad if there is an unused ad or one is already loading.
  if isLoadingAd || isAdAvailable() {
    return
  }
  isLoadingAd = true

  do {
    appOpenAd = try await GADAppOpenAd.load(
      withAdUnitID: "ca-app-pub-3940256099942544/5575463023", request: GADRequest())
  } catch {
    print("App open ad failed to load with error: \(error.localizedDescription)")
  }
  isLoadingAd = false
}

Objective-C

- (void)loadAd {
  // Do not load ad if there is an unused ad or one is already loading.
  if (self.isLoadingAd || [self isAdAvailable]) {
    return;
  }
  self.isLoadingAd = YES;

  [GADAppOpenAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/5575463023"
                       request:[GADRequest request]
             completionHandler:^(GADAppOpenAd *_Nullable appOpenAd, NSError *_Nullable error) {
               self.isLoadingAd = NO;
               if (error) {
                 NSLog(@"Failed to load app open ad: %@", error);
                 return;
               }
               self.appOpenAd = appOpenAd;
             }];
}

展示广告

下一步是填充 showAdIfAvailable() 方法。如果没有可展示的广告,该方法会尝试加载广告。

Swift

func showAdIfAvailable() {
  // If the app open ad is already showing, do not show the ad again.
  guard !isShowingAd else { return }

  // If the app open ad is not available yet but is supposed to show, load
  // a new ad.
  if !isAdAvailable() {
    Task {
      await loadAd()
    }
    return
  }

  if let ad = appOpenAd {
    isShowingAd = true
    ad.present(fromRootViewController: nil)
  }
}

Objective-C

- (void)showAdIfAvailable {
  // If the app open ad is already showing, do not show the ad again.
  if (self.isShowingAd) {
    return;
  }

  // If the app open ad is not available yet but is supposed to show, load a
  // new ad.
  if (![self isAdAvailable]) {
    [self loadAd];
    return;
  }

  self.isShowingAd = YES;
  [self.appOpenAd presentFromRootViewController:nil];
}

在应用进入前台事件期间展示广告

当应用变为活跃状态时,调用 showAdIfAvailable() 以显示广告(如果有)或加载新广告。

Swift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  // ...

  func applicationDidBecomeActive(_ application: UIApplication) {
    // Show the app open ad when the app is foregrounded.
    AppOpenAdManager.shared.showAdIfAvailable()
  }
}

Objective-C

@implementation AppDelegate
// ...

- (void) applicationDidBecomeActive:(UIApplication *)application {
  // Show the app open ad when the app is foregrounded.
  [AppOpenAdManager.sharedInstance showAdIfAvailable];
}

@end

处理展示回调

当应用展示开屏广告时,您应借助 GADFullScreenContentDelegate 处理某些展示事件。具体而言,您需要在第一个开屏广告展示完毕时请求下一个开屏广告。

AppOpenAdManager 类中,添加以下代码:

Swift

class AppOpenAdManager: NSObject, GADFullScreenContentDelegate {
  // ...

  private func loadAd() async {
    // Do not load ad if there is an unused ad or one is already loading.
    if isLoadingAd || isAdAvailable() {
      return
    }
    isLoadingAd = true

    do {
      appOpenAd = try await GADAppOpenAd.load(
        withAdUnitID: "ca-app-pub-3940256099942544/5575463023", request: GADRequest())
      appOpenAd?.fullScreenContentDelegate = self
    } catch {
      print("App open ad failed to load with error: \(error.localizedDescription)")
    }
    isLoadingAd = false
  }

  // ...

  // MARK: - GADFullScreenContentDelegate methods

  func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    print("App open ad will be presented.")
  }

  func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    appOpenAd = nil
    isShowingAd = false
    // Reload an ad.
    Task {
      await loadAd()
    }
  }

  func ad(
    _ ad: GADFullScreenPresentingAd,
    didFailToPresentFullScreenContentWithError error: Error
  ) {
    appOpenAd = nil
    isShowingAd = false
    // Reload an ad.
    Task {
      await loadAd()
    }
  }
}

Objective-C

@interface AppOpenAdManager () <GADFullScreenContentDelegate>
@property(nonatomic, strong) GADAppOpenAd *appOpenAd
@property(nonatomic, assign) BOOL isLoadingAd;
@property(nonatomic, assign) BOOL isShowingAd;

@end

@implementation AppOpenAdManager

// ...

- (void)loadAd {
  // Do not load ad if there is an unused ad or one is already loading.
  if (self.isLoadingAd || [self isAdAvailable]) {
    return;
  }
  self.isLoadingAd = YES;

  [GADAppOpenAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/5575463023"
                       request:[GADRequest request]
             completionHandler:^(GADAppOpenAd *_Nullable appOpenAd, NSError *_Nullable error) {
              self.isLoadingAd = NO;
               if (error) {
                 NSLog(@"Failed to load app open ad: %@", error);
                 return;
               }
               self.appOpenAd = appOpenAd;
               self.appOpenAd.fullScreenContentDelegate = self;
             }];
}

- (BOOL)isAdAvailable {
  // Check if ad exists and can be shown.
  return self.appOpenAd != nil;
}

// ...

#pragma mark - GADFullScreenContentDelegate methods

- (void)adWillPresentFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {
  NSLog(@"App open ad is will be presented.");
}

- (void)adDidDismissFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {
  self.appOpenAd = nil;
  self.isShowingAd = NO;
  // Reload an ad.
  [self loadAd];
}

- (void)ad:(nonnull id<GADFullScreenPresentingAd>)ad
    didFailToPresentFullScreenContentWithError:(nonnull NSError *)error {
  self.appOpenAd = nil;
  self.isShowingAd = NO;
  // Reload an ad.
  [self loadAd];
}

@end

考虑广告有效期

为确保您不会展示过期的广告,您可以在应用代理中添加一个方法,该方法会检查您的广告引用加载后经过的时间。

AppOpenAdManager 中,添加一个名为 loadTimeDate 属性,并在广告加载时设置该属性。接下来,您可以添加一个方法,该方法会在广告加载后经过的时间少于一定的小时数时返回 true。请务必先检查广告引用的有效性,然后再尝试展示广告。

Swift

class AppOpenAdManager: NSObject, GADFullScreenContentDelegate {
  var appOpenAd: GADAppOpenAd?
  var isLoadingAd = false.
  var isShowingAd = false
  var loadTime: Date?
  let fourHoursInSeconds = TimeInterval(3600 * 4)

  // ...

  private func loadAd() async {
    // Do not load ad if there is an unused ad or one is already loading.
    if isLoadingAd || isAdAvailable() {
      return
    }
    isLoadingAd = true

    do {
      appOpenAd = try await GADAppOpenAd.load(
        withAdUnitID: "ca-app-pub-3940256099942544/5575463023", request: GADRequest())
      appOpenAd?.fullScreenContentDelegate = self
      loadTime = Date()
    } catch {
      print("App open ad failed to load with error: \(error.localizedDescription)")
    }
    isLoadingAd = false
  }

  private func wasLoadTimeLessThanFourHoursAgo() -> Bool {
    guard let loadTime = loadTime else { return false }
    // Check if ad was loaded more than four hours ago.
    return Date().timeIntervalSince(loadTime) < fourHoursInSeconds
  }

  private func isAdAvailable() -> Bool {
    // Check if ad exists and can be shown.
    return appOpenAd != nil && wasLoadTimeLessThanFourHoursAgo()
  }
}

Objective-C

static NSTimeInterval const fourHoursInSeconds = 3600 * 4;

@interface AppOpenAdManager () <GADFullScreenContentDelegate>
@property(nonatomic, strong) GADAppOpenAd *appOpenAd
@property(nonatomic, assign) BOOL isLoadingAd;
@property(nonatomic, assign) BOOL isShowingAd;
@property(weak, nonatomic) NSDate *loadTime;

@end

@implementation AppOpenAdManager

// ...

- (void)loadAd {
  // Do not load ad if there is an unused ad or one is already loading.
  if (self.isLoadingAd || [self isAdAvailable]) {
    return;
  }
  self.isLoadingAd = YES;

  [GADAppOpenAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/5575463023"
                       request:[GADRequest request]
             completionHandler:^(GADAppOpenAd *_Nullable appOpenAd, NSError *_Nullable error) {
              self.isLoadingAd = NO;
               if (error) {
                 NSLog(@"Failed to load app open ad: %@", error);
                 return;
               }
               self.appOpenAd = appOpenAd;
               self.appOpenAd.fullScreenContentDelegate = self;
               self.loadTime = [NSDate date];
             }];
}

- (BOOL)wasLoadTimeLessThanFourHoursAgo {
  // Check if ad was loaded more than four hours ago.
  return [[NSDate Date] timeIntervalSinceDate:self.loadTime] < fourHoursInSeconds;
}

- (BOOL)isAdAvailable {
  // Check if ad exists and can be shown.
  return self.appOpenAd != nil && [self wasLoadTimeLessThanFourHoursAgo];
}

@end

冷启动和加载屏幕

本文档假定您仅在以下情况下展示开屏广告:用户将在内存中挂起的应用切换为在前台运行。用户启动您的应用,但该应用之前未在内存中挂起,这种情况就称为“冷启动”。

例如,用户首次打开您的应用便属于冷启动。对于冷启动,您没有之前已加载的开屏广告可供立即展示。请求广告和收到相应广告之间的延迟会导致出现以下情况:用户能够暂时使用您的应用,然后突然看到一条无关广告。应避免出现这种情况,因为这会导致用户体验不佳。

在冷启动时使用开屏广告的首选方法是,使用加载屏幕来加载游戏或应用素材资源,并且仅在加载屏幕展示广告。如果您的应用已加载完毕,并且用户已经看到应用的主要内容,则不要展示广告。

最佳做法

借助 Google 打造的开屏广告,您可以通过应用的加载屏幕创收。不过,还请务必考虑一些最佳实践,以便让用户喜欢使用您的应用。请务必遵循以下做法:

  • 等待用户使用几次您的应用后,再展示第一个开屏广告。
  • 利用用户等待您应用加载的那段时间,展示开屏广告。
  • 如果有加载屏幕位于开屏广告之下,并且加载屏幕在用户关闭广告之前已加载完毕,您可能需要通过 adDidDismissFullScreenContent 方法关闭加载屏幕。

GitHub 上的完整示例

Swift Objective-C

后续步骤

不妨详细了解用户隐私权