應用程式開啟頁面廣告

選取平台: Android iOS Unity Flutter

本指南適用對象:有意整合應用程式開啟頁面廣告的發布商。

「應用程式開啟頁面廣告」是一種特殊廣告格式,適合想利用應用程式載入畫面營利的發布商。這類廣告可在使用者將應用程式切換至前景時出現,使用者隨時可以自行關閉。

應用程式開啟頁面廣告會在一小塊區域自動顯示品牌資訊,方便使用者判斷目前仍在應用程式內。以下是這類廣告範例:

應用程式開啟頁面廣告的導入步驟簡要說明如下:

  1. 建立管理器類別,用來預先載入需要顯示的廣告。
  2. 在應用程式進入前景時顯示廣告。
  3. 處理顯示事件回呼。

必備條件

請務必使用測試廣告進行測試

建構及測試應用程式時,請務必使用測試廣告,而非實際運作中的廣告,否則可能導致帳戶遭停權。

如要載入測試廣告,最簡單的方法是使用應用程式開啟頁面廣告專用的測試廣告單元 ID:

ca-app-pub-3940256099942544/5575463023

測試 ID 經特別設定,可針對每個請求傳回測試廣告。您可在編寫程式碼、測試及偵錯時,在自家應用程式中使用這些 ID。但別忘了在發布應用程式前,將這類 ID 替換為自己的廣告單元 ID。

如要進一步瞭解 Mobile Ads SDK 測試廣告的運作方式,請參閱「測試廣告」。

實作管理器類別

廣告應盡快顯示,因此最好能在放送前預先載入,在使用者一進入應用程式時立即出現。您可實作管理器類別來預先發出廣告請求,事先做好顯示廣告的準備。

請建立名為 AppOpenAdManager 的新單例模式類別:

Swift

class AppOpenAdManager: NSObject {
  /// The app open ad.
  var appOpenAd: AppOpenAd?
  /// Maintains a reference to the delegate.
  weak var appOpenAdManagerDelegate: AppOpenAdManagerDelegate?
  /// Keeps track of if an app open ad is loading.
  var isLoadingAd = false
  /// Keeps track of if an app open ad is showing.
  var isShowingAd = false
  /// Keeps track of the time when an app open ad was loaded to discard expired ad.
  var loadTime: Date?
  /// For more interval details, see https://support.google.com/admob/answer/9341964
  let timeoutInterval: TimeInterval = 4 * 3_600

  static let shared = AppOpenAdManager()

Objective-C

@interface AppOpenAdManager ()

/// The app open ad.
@property(nonatomic, strong, nullable) GADAppOpenAd *appOpenAd;
/// Keeps track of if an app open ad is loading.
@property(nonatomic, assign) BOOL isLoadingAd;
/// Keeps track of if an app open ad is showing.
@property(nonatomic, assign) BOOL isShowingAd;
/// Keeps track of the time when an app open ad was loaded to discard expired ad.
@property(nonatomic, strong, nullable) NSDate *loadTime;

@end

/// For more interval details, see https://support.google.com/admob/answer/9341964
static const NSInteger kTimeoutInterval = 4;

@implementation AppOpenAdManager

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

實作相應的 AppOpenAdManagerDelegate 通訊協定:

Swift

protocol AppOpenAdManagerDelegate: AnyObject {
  /// Method to be invoked when an app open ad life cycle is complete (i.e. dismissed or fails to
  /// show).
  func appOpenAdManagerAdDidComplete(_ appOpenAdManager: AppOpenAdManager)
}

Objective-C

@protocol AppOpenAdManagerDelegate <NSObject>
/// Method to be invoked when an app open ad life cycle is complete (i.e. dismissed or fails to
/// show).
- (void)adDidComplete;
@end

載入廣告

下一步是載入應用程式開啟頁面廣告:

Swift

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 AppOpenAd.load(
      with: "ca-app-pub-3940256099942544/5575463023", request: Request())
    appOpenAd?.fullScreenContentDelegate = self
    loadTime = Date()
  } catch {
    print("App open ad failed to load with error: \(error.localizedDescription)")
    appOpenAd = nil
    loadTime = nil
  }
  isLoadingAd = false
}

Objective-C

- (void)loadAd {
  // Do not load ad if there is an unused ad or one is already loading.
  if ([self isAdAvailable] || self.isLoadingAd) {
    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(@"App open ad failed to load with error: %@", error);
      self.appOpenAd = nil;
      self.loadTime = nil;
      return;
    }
    self.appOpenAd = appOpenAd;
    self.appOpenAd.fullScreenContentDelegate = self;
    self.loadTime = [NSDate date];
  }];
}

顯示廣告

下一步是顯示應用程式開啟頁面廣告。如果沒有可用廣告,則嘗試載入新廣告。

Swift

func showAdIfAvailable() {
  // If the app open ad is already showing, do not show the ad again.
  if isShowingAd {
    return print("App open ad is already showing.")
  }

  // If the app open ad is not available yet but is supposed to show, load
  // a new ad.
  if !isAdAvailable() {
    print("App open ad is not ready yet.")
    // The app open ad is considered to be complete in this example.
    appOpenAdManagerDelegate?.appOpenAdManagerAdDidComplete(self)
    // Load a new ad.
    return
  }

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

Objective-C

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

  // If the app open ad is not available yet but is supposed to show, load
  // a new ad.
  if (![self isAdAvailable]) {
    NSLog(@"App open ad is not ready yet.");
    // The app open ad is considered to be complete in this example.
    [self adDidComplete];
    // Load a new ad.
    return;
  }

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

在應用程式進入前景時顯示廣告

應用程式進入前景時,系統會呼叫 showAdIfAvailable() 來顯示可用的廣告。若沒有廣告可用,則載入新廣告。

Swift

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

Objective-C

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

處理顯示事件回呼

為了接收廣告顯示事件的通知,您需要將 GADFullScreenContentDelegate 指派給傳回廣告的 fullScreenContentDelegate 屬性:

Swift

appOpenAd?.fullScreenContentDelegate = self

Objective-C

self.appOpenAd.fullScreenContentDelegate = self;

具體來說,您需要在第一則應用程式開啟頁面廣告展示完成後,接著請求下一則廣告。請參考下方程式碼,瞭解如何在 AppOpenAdManager 檔案中導入通訊協定:

Swift

func adDidRecordImpression(_ ad: FullScreenPresentingAd) {
  print("App open ad recorded an impression.")
}

func adDidRecordClick(_ ad: FullScreenPresentingAd) {
  print("App open ad recorded a click.")
}

func adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
  print("App open ad will be dismissed.")
}

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

func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
  print("App open ad was dismissed.")
  appOpenAd = nil
  isShowingAd = false
  appOpenAdManagerDelegate?.appOpenAdManagerAdDidComplete(self)
  Task {
    await loadAd()
  }
}

func ad(
  _ ad: FullScreenPresentingAd,
  didFailToPresentFullScreenContentWithError error: Error
) {
  print("App open ad failed to present with error: \(error.localizedDescription)")
  appOpenAd = nil
  isShowingAd = false
  appOpenAdManagerDelegate?.appOpenAdManagerAdDidComplete(self)
  Task {
    await loadAd()
  }
}

Objective-C

- (void)adDidRecordImpression:(nonnull id<GADFullScreenPresentingAd>)ad {
  NSLog(@"App open ad recorded an impression.");
}

- (void)adDidRecordClick:(nonnull id<GADFullScreenPresentingAd>)ad {
  NSLog(@"App open ad recorded a click.");
}

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

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

- (void)adDidDismissFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {
  NSLog(@"App open ad was dismissed.");
  self.appOpenAd = nil;
  self.isShowingAd = NO;
  [self adDidComplete];
  [self loadAd];
}

- (void)ad:(nonnull id<GADFullScreenPresentingAd>)ad
    didFailToPresentFullScreenContentWithError:(nonnull NSError *)error {
  NSLog(@"App open ad failed to present with error: %@", error.localizedDescription);
  self.appOpenAd = nil;
  self.isShowingAd = NO;
  [self adDidComplete];
  [self loadAd];
}

考慮廣告效期

為避免顯示失效的廣告,您可以在應用程式委派物件中導入方法,檢查廣告參照載入後過了多久。

請在 AppOpenAdManager 中加入名為 loadTimeDate 屬性,並在廣告載入時設定該屬性。接著新增方法,檢查廣告載入後經過的時間是否超過指定長度,若未超過則傳回 true。顯示廣告前,請務必確認廣告參照仍然有效。

Swift

private func wasLoadTimeLessThanNHoursAgo(timeoutInterval: TimeInterval) -> Bool {
  // Check if ad was loaded more than n hours ago.
  if let loadTime = loadTime {
    return Date().timeIntervalSince(loadTime) < timeoutInterval
  }
  return false
}

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

Objective-C

- (BOOL)wasLoadTimeLessThanNHoursAgo:(int)n {
  // Check if ad was loaded more than n hours ago.
  NSDate *now = [NSDate date];
  NSTimeInterval timeIntervalBetweenNowAndLoadTime = [now timeIntervalSinceDate:self.loadTime];
  double secondsPerHour = 3600.0;
  double intervalInHours = timeIntervalBetweenNowAndLoadTime / secondsPerHour;
  return intervalInHours < n;
}

- (BOOL)isAdAvailable {
  // Check if ad exists and can be shown.
  return _appOpenAd && [self wasLoadTimeLessThanNHoursAgo:kTimeoutInterval];
}

冷啟動和載入畫面

本文假設的應用程式開啟頁面廣告出現時機,均為「使用者將暫存於記憶體中的應用程式切換到前景時」。如果應用程式啟動時,不是從記憶體中的暫存狀態恢復到前景,即所謂的「冷啟動」。

舉例來說,使用者初次開啟應用程式就是冷啟動,這時不會有預先載入的應用程式開啟頁面廣告,因此無法立即顯示。從請求到收到廣告之間會有延遲,使用者在這一小段期間可使用應用程式,但接著會突然看到與內容無關的廣告。這種情況會造成使用者體驗不佳,建議避免。

要在冷啟動時放送應用程式開啟頁面廣告,建議使用載入畫面,載入遊戲或應用程式素材資源,並只在載入畫面中顯示廣告。如果應用程式已載入完成,並將使用者帶往應用程式的主要內容,請勿顯示廣告。

最佳做法

Google 打造應用程式開啟頁面廣告的用意,在於協助發布商利用應用程式的載入畫面營利。為確保良好的使用者體驗,發布商應遵循以下最佳做法:

  • 等使用者用過應用程式數次後,再顯示第一則應用程式開啟頁面廣告。
  • 在既有的應用程式載入期間,顯示應用程式開啟頁面廣告。
  • 如果載入畫面是在應用程式開啟頁面廣告的背景執行,且在使用者關閉廣告前載入完成,建議以 adDidDismissFullScreenContent 方法關閉載入畫面。

GitHub 上的完整範例

Swift Objective-C

後續步驟

進一步瞭解使用者隱私