Flutter: AdMobインタースティシャル広告のload完了をawaitで待てない

結論:Completerをコールバック関数内に設置して完了を待たせる

2023/1/28 Flutter エラー・バグ日記

 

AdMobインタースティシャル広告を表示するには、あらかじめloadメソッド(非同期・戻り値なしのFuture<void>型のメソッド)で、広告データをロード(読み込む)必要がある。

 

ロードが終わらずに、広告を表示させるshowメソッドを実行しても、広告は表示されないので、awaitでloadメソッドの完了を待ってから、次の処理を実行しようとした。

 

しかし、なぜかawaitが機能せず、ロードが終わる前に次の処理に進んでしまう

 

バナー広告のloadメソッドは、awaitできちんとロード完了を待てるのだが、インタースティシャル広告では待つことができない。。

 

本記事はライトな日記思考で書いているので、詳細説明はしておらず、基本、テキストのみで画像とかはあまり載せておりません。。m(_ _)m

解説記事ではないため、解決していない内容や、その時々の間違った解釈を述べてしまっている可能性が大いにありますので、何卒、ご了承ください。

 

awaitが機能しないときのコード

loadメソッド部分のコードは、「google_mobile_ads」パッケージのExampleコード

 

 

を参考にして、以下のように記述していた(ロードメソッド部分のみ抜粋)。

 

// クラス名、メソッド名、プロパティ名(変数名)について、筆者が作成したもの(名前変更可のもの)
// の名前の末尾には、大文字のオー「O」をつけています
// ※ライブラリ(パッケージ)で予め決められているもの(名前の変更不可のもの)と、
//  自分で作成したもの(名前の変更可のもの)の区別をしやすくするため

  // インタースティシャル広告のデータを入れるプロパティ
  InterstitialAd? interstitialAdO;
  
  // インタースティシャル広告の試行最大回数と試行回数
  int maxFailedToAttemptO = 3;
  int _numInterstitialLoadAttemptO = 0;

 // インタースティシャル広告のロード処理のために作成したメソッド
 Future<void> initInterstitialAdO() async{

    // インタースティシャル広告のロードメソッド
    // ロード完了を待つために、awaitをつける
    await InterstitialAd.load(

      // ここではAndroid用のテスト広告IDを記載
      adUnitId: "ca-app-pub-3940256099942544/1033173712",

      request: const AdRequest(),

      adLoadCallback: InterstitialAdLoadCallback(

        // ロード成功時のコールバック関数
        onAdLoaded: (InterstitialAd adO) {

          // 広告データの代入
          interstitialAdO = adO;

          // 成功したので、試行回数カウントを初期化する
          _numInterstitialLoadAttemptO = 0;

          debugPrint("インタースティシャルadロード完了");
        },

        // ロード失敗時のコールバック関数
        onAdFailedToLoad: (LoadAdError errorO) {
          debugPrint("インタースティシャルadロードできず");

          // 念のため広告データを初期化
          interstitialAdO = null;

          // 失敗したので、3回まで試行するため、カウントを増やす
          _numInterstitialLoadAttemptO++;

          // 3回以内ならロードメソッドを再帰的に呼ぶ
          if (_numInterstitialLoadAttemptO <= maxFailedToAttemptO) {
            initInterstitialAdO();
          }
        },
      ),
    );
  }

  // 以降、ロード完了後の処理
  // ・・・・・・

 

Exampleのコードでは、async awaitは付いていないが、「interstitialAdO」に広告データが格納された後に、次の処理に進んでほしいので、「InterstitialAd.load」メソッドにasync awaitを付けておいた

 

しかし、実行すると「interstitialAdO」がnullのままで(広告データが入る前に)、先に進んでしまった。。

 

「onAdLoaded:」のコールバック関数内に、ロード完了時のコメントをdebugPrintで仕込んだところ、次の処理の実行中にコメントが表示されたため、awaitが効いていないことが確認できた。

 

コールバック関数内の処理完了を待つには、Completerクラスを使う必要あり

調べてみると、全く同じ悩みに関するQA記事があった(大変助かりましたm(_ _)m)。

 

 

「Completer」というクラスのインスタンスを作った上で、loadメソッドの引数である「onAdLoaded:」と「onAdFailedToLoad:」の各コールバック関数内に、完了地点を設置すれば良いらしい。

 

loadメソッドに対してawaitを付けるだけだと、コールバック関数のように、間接的な位置にある非同期処理の終了までは、待てないらしい。

 

恥ずかしながら、「Completer」というクラスは初めて知った。。

  

こちらの記事で詳しく説明されており、大変勉強になった(ありがとうございますm(_ _)m)。

 

 

 

非同期処理を自作したいとき(まさに、コールバック関数からの非同期での返り値取得など)に用いるらしい。

 

「Completer」クラスのFlutter公式の説明は以下。

 

 

確かに、前述のQA記事を参考に、下記のとおりコードを修正したら、loadメソッド完了を待って、次の処理を実行できるようになった。

 

// 前述のコードから追加した部分のみ、コメントを記載

  InterstitialAd? interstitialAdO;
  
  int maxFailedToAttemptO = 3;
  int _numInterstitialLoadAttemptO = 0;

 Future<void> initInterstitialAdO() async{

    // 【追加】 Completer(戻り値なし)のインスタンス作成
    var adCompleterO = Completer<void>();

    await InterstitialAd.load(
      adUnitId: "ca-app-pub-3940256099942544/1033173712",
      request: const AdRequest(),
      adLoadCallback: InterstitialAdLoadCallback(

        onAdLoaded: (InterstitialAd adO) {
          interstitialAdO = adO;
          _numInterstitialLoadAttemptO = 0;
          debugPrint("インタースティシャルadロード完了");

          // 【追加】 ロード成功時の完了地点の設置: ここまでの処理が終わったら完了とする
          adCompleterO.complete();
        },

        onAdFailedToLoad: (LoadAdError errorO) {
          debugPrint("インタースティシャルadロードできず");
          interstitialAdO = null;
          _numInterstitialLoadAttemptO++;
          if (_numInterstitialLoadAttemptO <= maxFailedToAttemptO) {
            initInterstitialAdO();
          }

          // 【追加】 ロード失敗時の完了地点の設置: エラーログを返す
          adCompleterO.completeError(errorO);
        },
      ),
    );

    //  【追加】 上記完了地点に到達した時点で、このメソッド全体を終了(voidのため戻り値はなし)
    await adCompleterO.future;
  }

  // 以降、ロード完了後の処理
  // ・・・・・・

 

若干修正が必要:AdMob審査期間中は、loadできないと処理が完了しない

上記コードだと1点問題があり、広告データがloadできなかった場合に、処理が完了せず、アプリが止まってしまった。

 

アプリをリリースした直後は、AdMobの管理画面にストア情報を登録し、AdMob側で審査してもらう必要がある。

 

審査期間中は、広告が表示されないため、ロード失敗時の「onAdFailedToLoad:」に設定したコールバック関数が実行されるのだが、この処理が完了扱いにならず、次の処理に進まなくなっていた

 

結論として、完了地点に設定した「completeError」メソッドを、ロード成功時の「complete」メソッドに変更したところ、広告がロードできない状況でも、次の処理に進むようになった。

  

最終的な修正後のコードは以下のとおり。

 

// 1つ前のコードから修正した部分のみ、コメントを記載

  InterstitialAd? interstitialAdO;
  
  int maxFailedToAttemptO = 3;
  int _numInterstitialLoadAttemptO = 0;

 Future<void> initInterstitialAdO() async{

    var adCompleterO = Completer<void>();

    await InterstitialAd.load(
      adUnitId: "ca-app-pub-3940256099942544/1033173712",
      request: const AdRequest(),
      adLoadCallback: InterstitialAdLoadCallback(

        onAdLoaded: (InterstitialAd adO) {
          interstitialAdO = adO;
          _numInterstitialLoadAttemptO = 0;
          debugPrint("インタースティシャルadロード完了");

          adCompleterO.complete();
        },

        onAdFailedToLoad: (LoadAdError errorO) {
          debugPrint("インタースティシャルadロードできず");
          interstitialAdO = null;
          _numInterstitialLoadAttemptO++;
          if (_numInterstitialLoadAttemptO <= maxFailedToAttemptO) {
            initInterstitialAdO();
          }

          // adCompleterO.completeError(errorO);
          // 【修正】 以下に修正
          adCompleterO.complete();
        },
      ),
    );

    await adCompleterO.future;
  }

  // 以降、ロード完了後の処理
  // ・・・・・・

 

AdMobの審査承認後、「onAdFailedToLoad:」の完了設定を「completeError」メソッドに戻し、あえてネットワークの接続を切って実験すると、アプリは停止せず、次の処理に進むことができた。

 

十分検証しきれていないが、AdMob審査中の状況だと「completeError」メソッドがうまく動作しないのかもしれない。。(間違っていそうですが、、m(_ _)m)

 

エラーログが無くても、「interstitialAdO」がnullの場合に、showメソッドを発動しないよう制御できるので、とりあえずエラーログが不要なら、ロード失敗時の完了設定も「complete」メソッドにしておく方が無難、というのが現状の理解。

 

\一般的なエラー対処法をまとめた記事はこちら/

 

リリースしたアプリ(全てFlutterで開発)

OCR文字認識Fast -シンプルな高精度・日本語スキャナー

Google Play で手に入れよう
Download on the App Store

 

暗記用マーカー - シンプル穴埋め問題作成

Google Play で手に入れよう
Download on the App Store

 

超即ToDo –最短2タップで通知登録できるタスク管理アプリ

Google Play で手に入れよう
Download on the App Store

 

かんたんプリント管理:アラート・OCR文字認識・検索機能を搭載

Google Play で手に入れよう
Download on the App Store

 

シンプルメモ帳「BasicMemo」 - 文字カウント、ワンタッチ入力、タグ管理等の機能を搭載

Macのデスクトップ版もリリースしました。

Google Play で手に入れよう
Download on the App Store

 

個人アプリ開発で役立ったもの

おすすめの学習教材

 \キャンペーン時を狙えば安価で本場の内容が学べる/

 

超初心者向けでオススメな元Udemyの講座/

 

\Gitの基礎について無料で学べる/

 

おすすめの学習書籍

実用的image_pickerに関してかなり助けられた/

 

Dartの基礎文法を素早くインプットできる/


Dart入門 - Dartの要点をつかむためのクイックツアー

 

おすすめのソフトウェア

安くて高機能。アプリの独自ドメイン・紹介サイト構築に最適/

 

\アイコン作成・画面設計・クラウド保存...何でもできて超必須

Microsoft Public Affiliate Program (JP)(マイクロソフトアフィリエイトプログラム)

 

おすすめのハードウェア

\リーズナブルな価格で検証端末を確保できる/

 

\目線の高さを調節しやすく、疲れにくい

 

\キータッチが超静音で心地よい/

 

おすすめのサポートアイテム

\部屋の中を仕切って、集中できる開発環境を作れる/

 

\部屋の中でも大き過ぎず、長時間座っても疲れない

 

\バグと格闘した後の肩こりを解消してくれる/

タイトルとURLをコピーしました