アプリ起動時広告

アプリ起動時広告は、アプリの読み込み画面を収益化することを望むパブリッシャー様を対象とした広告フォーマットです。この広告は、ユーザーがアプリをフォアグラウンドに移動すると表示され、いつでも閉じることができます。

アプリ起動時広告ではアプリのブランディング エリアが自動的に表示されるため、ユーザーはアプリが起動中であることがわかります。以下は、アプリ起動時広告の表示例です。

前提条件

  • Flutter プラグイン 0.13.6 以降。
  • スタートガイドの手順を完了し、Flutter アプリに Google モバイル広告の Flutter プラグインを事前にインポートしておく必要があります。

常にテスト広告でテストする

アプリの開発とテストでは必ずテスト広告を使用し、配信中の実際の広告は使用しないでください。実際の広告を使用すると、アカウントが停止される可能性があります。

テスト広告を読み込む際は、Android と iOS にそれぞれ対応したリワード広告向けのテスト専用広告ユニット ID を使うと簡単です。

Android

ca-app-pub-3940256099942544/9257395921

iOS

ca-app-pub-3940256099942544/5575463023

この ID は、すべてのリクエストに対してテスト広告を返す特別な ID で、アプリのコーディング、テスト、デバッグで自由に使うことができます。なお、このテスト用 ID は、アプリを公開する前に必ずご自身の広告ユニット ID に置き換えてください。

実装

アプリ起動時広告を組み込む主な手順は、以下のとおりです。

  1. 広告を表示するタイミングの前に広告を読み込むユーティリティ クラスを作成します。
  2. 広告を読み込みます。
  3. コールバックを登録して広告を表示します。
  4. フォアグラウンド イベント中に広告を表示するよう、AppStateEventNotifier.appStateStream に登録します。

ユーティリティ クラスを作成する

AppOpenAdManager という新しいクラスを作成して、広告を読み込みます。このクラスにより、読み込まれた広告と、各プラットフォームの広告ユニット ID をトラッキングするためのインスタンス変数が管理されます。

import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io' show Platform;

class AppOpenAdManager {
  
  String adUnitId = Platform.isAndroid
    ? 'ca-app-pub-3940256099942544/9257395921'
    : 'ca-app-pub-3940256099942544/5575463023';
  
  AppOpenAd? _appOpenAd;
  bool _isShowingAd = false;

  /// Load an AppOpenAd.
  void loadAd() {
    // We will implement this below.
  }

  /// Whether an ad is available to be shown.
  bool get isAdAvailable {
    return _appOpenAd != null;
  }
}

広告を読み込む

アプリ起動時広告は、ユーザーがアプリを開く前に、表示できる状態になっている必要があります。ユーティリティ クラスを実装して、広告を表示する必要があるタイミングに先立って広告リクエストが行われるようにします。

広告の読み込みは、AppOpenAd クラスで load メソッドを使用して行います。読み込みメソッドには、広告ユニット ID、オリエンテーション モード(画面の向き)、AdRequest オブジェクトのほか、広告の読み込みの成功時または失敗時に呼び出される完了ハンドラが必要です。読み込まれた AppOpenAd オブジェクトは、完了ハンドラのパラメータとして提供されます。次の例は、AppOpenAd を読み込む方法を示しています。

public class AppOpenAdManager {
  ...

  /// Load an AppOpenAd.
  void loadAd() {
    AppOpenAd.load(
      adUnitId: adUnitId,
      adRequest: AdRequest(),
      adLoadCallback: AppOpenAdLoadCallback(
        onAdLoaded: (ad) {
          _appOpenAd = ad;
        },
        onAdFailedToLoad: (error) {
          print('AppOpenAd failed to load: $error');
          // Handle the error.
        },
      ),
    );
  }
}

広告を表示して全画面表示コールバックを処理する

広告を表示する前に、リッスンする広告イベントごとに FullScreenContentCallback を登録します。

public class AppOpenAdManager {
  ...

  public void showAdIfAvailable() {
    if (!isAdAvailable) {
      print('Tried to show ad before available.');
      loadAd();
      return;
    }
    if (_isShowingAd) {
      print('Tried to show ad while already showing an ad.');
      return;
    }
    // Set the fullScreenContentCallback and show the ad.
    _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(
      onAdShowedFullScreenContent: (ad) {
        _isShowingAd = true;
        print('$ad onAdShowedFullScreenContent');
      },
      onAdFailedToShowFullScreenContent: (ad, error) {
        print('$ad onAdFailedToShowFullScreenContent: $error');
        _isShowingAd = false;
        ad.dispose();
        _appOpenAd = null;
      },
      onAdDismissedFullScreenContent: (ad) {
        print('$ad onAdDismissedFullScreenContent');
        _isShowingAd = false;
        ad.dispose();
        _appOpenAd = null;
        loadAd();
      },
    );
  }
}

これにより、ユーザーがアプリ起動時広告をクリックしてからアプリに戻った場合に、別のアプリ起動時広告がユーザーに表示されることがなくなります。

アプリのフォアグラウンド イベントをリッスンする

アプリのフォアグラウンド イベントの通知を受け取るには、AppStateEventNotifier.appStateStream に登録し、foreground イベントをリッスンする必要があります。

import 'package:app_open_example/app_open_ad_manager.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

/// Listens for app foreground events and shows app open ads.
class AppLifecycleReactor {
  final AppOpenAdManager appOpenAdManager;

  AppLifecycleReactor({required this.appOpenAdManager});

  void listenToAppStateChanges() {
    AppStateEventNotifier.startListening();
    AppStateEventNotifier.appStateStream
        .forEach((state) => _onAppStateChanged(state));
  }

  void _onAppStateChanged(AppState appState) {
    // Try to show an app open ad if the app is being resumed and
    // we're not already showing an app open ad.
    if (appState == AppState.foreground) {
      appOpenAdManager.showAdIfAvailable();
    }
  }
}

これで、AppLifecycleReactor を初期化して、アプリのライフサイクルの変化をリッスンできるようになりました。たとえば、ホームページから次のように追加できます。

import 'package:app_open_example/app_open_ad_manager.dart';
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

import 'app_lifecycle_reactor.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'App Open Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'App Open Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

/// Example home page for an app open ad.
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late AppLifecycleReactor _appLifecycleReactor;

  @override
  void initState() {
    super.initState();
    
    AppOpenAdManager appOpenAdManager = AppOpenAdManager()..loadAd();
    _appLifecycleReactor = AppLifecycleReactor(
      appOpenAdManager: appOpenAdManager);
  }

広告の有効期限に関する考察

期限切れの広告を表示しないようにするには、タイムスタンプを AppOpenAdManager に追加して、広告を読み込んでから経過した時間と広告の有効性を確認できるようにします。

/// Utility class that manages loading and showing app open ads.
class AppOpenAdManager {
  ...
  
  /// Maximum duration allowed between loading and showing the ad.
  final Duration maxCacheDuration = Duration(hours: 4);

  /// Keep track of load time so we don't show an expired ad.
  DateTime? _appOpenLoadTime;
  
  ...

  /// Load an AppOpenAd.
  void loadAd() {
    AppOpenAd.load(
      adUnitId: adUnitId,
      orientation: AppOpenAd.orientationPortrait,
      adRequest: AdRequest(),
      adLoadCallback: AppOpenAdLoadCallback(
        onAdLoaded: (ad) {
          print('$ad loaded');
          _appOpenLoadTime = DateTime.now();
          _appOpenAd = ad;
        },
        onAdFailedToLoad: (error) {
          print('AppOpenAd failed to load: $error');
        },
      ),
    );
  }

  /// Shows the ad, if one exists and is not already being shown.
  ///
  /// If the previously cached ad has expired, this just loads and caches a
  /// new ad.
  void showAdIfAvailable() {
    if (!isAdAvailable) {
      print('Tried to show ad before available.');
      loadAd();
      return;
    }
    if (_isShowingAd) {
      print('Tried to show ad while already showing an ad.');
      return;
    }
    if (DateTime.now().subtract(maxCacheDuration).isAfter(_appOpenLoadTime!)) {
      print('Maximum cache duration exceeded. Loading another ad.');
      _appOpenAd!.dispose();
      _appOpenAd = null;
      loadAd();
      return;
    }
    // Set the fullScreenContentCallback and show the ad.
    _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(...);
    _appOpenAd!.show();
  }
}

コールド スタートと読み込み画面

このドキュメントではこれまでのところ、アプリ起動時広告が表示されるのは、メモリで一時停止していたアプリがフォアグラウンドに移動した場合のみを想定していました。「コールド スタート」は、メモリ内で一時停止されていなかったアプリが起動された場合に発生します。

コールド スタートの例として、ユーザーが初めてアプリを開くときを挙げることができます。 コールド スタートでは、直ちに表示することのできる読み込み済みのアプリ起動時広告がありません。広告をリクエストしてから広告が返されるまでに時間差があるため、ユーザーが少しだけアプリを使用したところで突然コンテキストから外れた広告が表示されることになってしまいます。ユーザー エクスペリエンスの低下につながるこのような事態は、避ける必要があります。

コールド スタートでアプリ起動時広告を使用する場合のおすすめの方法は、ゲームまたはアプリのアセットを読み込んでいる読み込み画面からのみ広告を表示することです。アプリの読み込みが完了し、アプリのメイン コンテンツが開いたら、そこには広告を表示しないでください。

ベスト プラクティス

アプリ起動時広告では、アプリの初回起動時やアプリの切り替え時にアプリの読み込み画面が収益化されますが、ユーザーがアプリをスムーズに使用できるよう、以下のベスト プラクティスを念頭に置いてください。

  • ユーザーがアプリを数回使用した後に、アプリ起動時広告を初めて表示する。
  • ユーザーが本来ならアプリの読み込みを待機しているだけの間に、アプリ起動時広告を表示します。
  • アプリ起動時広告の下に読み込み画面が存在し、広告が閉じられる前に読み込み画面の読み込みが完了した場合は、onAdDismissedFullScreenContent イベント ハンドラで読み込み画面を閉じることができます。

GitHub のサンプルコードの全文

アプリ起動