Flutterで広告実装2 ~Android EmulatorでAdMobが表示されない~

flutterでAdMobのバナーを表示してみるテスト。 emulatorのOSイメージの設定でバナーが表示されない問題にハマった。参考記事もあまり見当たらなかった。私だけか。。? とりあえず実装の流れ。

ライブラリ

flutter上でadmobを実装するためのライブラリは数種類あるが、公式から新しいのでたらしいのでgoogle_mobile_adsを使う。 Google Mobile Ads for Flutter

インストール

flutter pub get google_mobile_ads

設定

Mobile Ads SDK(Android) 公式を参考に app/build.gradle, AndroidManifest.xmlに追記

これだけだとエラー出てくると思う。 google_mobile_adsを使うにはkotlinとかsdkのバージョンを上げろ的なことを言われる。

android/app/build.gradle
android {
    compileSdkVersion 31 // 変更

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.testtest"
        minSdkVersion 19 // 変更
        targetSdkVersion 30 // 変更
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
    
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.google.android.gms:play-services-ads:20.6.0' // 追加
}

デフォだとflutter.gradleから値を持って来られるが直書きでオーバーライドしてしまうと flutter runが通るようになる。

実装

こんな感じでBLOC実装。デバッグの時はdebug用のIDを読めるような仕組みにする。

ad_state.dart
abstract class AdDictAbs {
  String get banner;
}

// android
class adDictAndroid implements AdDictAbs {
  
  final String banner = 'ca-app-pub-xxxxxxxxx/yyyyyyyyyy';
}

class adDictAndroidDebug implements AdDictAbs {
  
  final String banner = 'ca-app-pub-3940256099942544/6300978111';
}

// ios
class adDictiOS implements AdDictAbs {
  
  final String banner = '#';
}

class adDictiOSDebug implements AdDictAbs {
  
  final String banner = 'ca-app-pub-3940256099942544/2934735716';
}

class AdState {
  Future<InitializationStatus> initialization;
  AdState(this.initialization);
  // String get bannerAdUnitId => Platform.isAndroid

}
bool debugMode = true;

class AdDict {
  static AdDictAbs Android =
      (debugMode) ? adDictAndroidDebug() : adDictAndroid();

  static AdDictAbs iOS = (debugMode) ? adDictiOSDebug() : adDictiOS();
}


class AdBloc {
  final _bannerSubject = BehaviorSubject<BannerAd?>();
  Stream<BannerAd?> get bannerStream => _bannerSubject.stream;

  void bannerInit() {
    AdRequest _request = const AdRequest();

    print('banner init !!! bannerid ${AdDict.Android.banner}');
    BannerAd(
            size: AdSize.banner,
            adUnitId: AdDict.Android.banner,
            listener: BannerAdListener(
              onAdLoaded: (ad) {
                print("onADLOADEDD !!!!! bannerad $ad");
                _bannerSubject.sink.add(ad as BannerAd);
              },
              onAdFailedToLoad: (Ad ad, LoadAdError error) {
                print('$BannerAd failedToLoad!!!!!: $error');
                _bannerSubject.value?.dispose();
              },
              onAdOpened: (Ad ad) => print('$BannerAd onAdOpened'),
              onAdClosed: (Ad ad) => print('$BannerAd onAdClosed'),
            ),
            request: _request)
        .load();
  }

  void dispose() {
    _bannerSubject.value?.dispose();
    _bannerSubject.close();
  }
}

widget部分はこんな。

homepage.dart
class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);
  
  Widget build(BuildContext context) {
    final adbloc = Provider.of<AdBloc>(context);
    
    void testinit() async {
      await Future.delayed(const Duration(milliseconds: 100));
      adbloc.bannerInit();
    }

    testinit();

    return Scaffold(
      backgroundColor: tabBg,
      body: Container(
        child: Text('Main Body Area Hello')
      ),
      bottomNavigationBar: Container(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text('bottom banner'),
            StreamBuilder<BannerAd?>(
              stream: adbloc.bannerStream,
              builder: (context, snapshot) {
                BannerAd? ad = snapshot.data;
                print('adad $ad');
                if (ad == null) return Container();
                return SizedBox(
                  width: ad.size.width.toDouble(),
                  height: ad.size.height.toDouble(),
                  child: AdWidget(ad: ad),
                );
              },
            )
          ],
        ),
      ),
    );
  }
}

エラー

表示できるかと思いきやエラー。

W/GooglePlayServicesUtil(28945): com.example.testtest requires the Google Play Store, but it is missing.
I/Ads     (28945): Ad failed to load : 0
I/flutter (28945): BannerAd failedToLoad!!!!!: LoadAdError(code: 0, domain: com.google.android.gms.ads, message: Internal error., responseInfo: ResponseInfo(responseId: null, mediationAdapterClassName: , adapterResponses: []))

google play storeに登録しないと表示できない雰囲気か?と思いつつ

ここをチェック 広告が表示されない場合の一般的な理由 しかし良い回答がない。


どうやらemulatorだと確認できない?

実機で確認してみたところ

W/Ads     ( 9399): Not retrying to fetch app settings
I/Ads     ( 9399): Ad failed to load : 3
I/flutter ( 9399): BannerAd failedToLoad!!!!!: LoadAdError(code: 3, domain: com.google.android.gms.ads, message: Publisher data not found. <https://support.google.com/admob/answer/9905175#9>, responseInfo: ResponseInfo(responseId: null, mediationAdapterClassName: , adapterResponses: []))

publisher data not found.

パブリッシャー データが見つかりません 意味: この広告ユニットのパブリッシャーのデータが見つかりません。広告ユニットを追加して間もない場合は、広告が表示されるまでに時間がかかることがあります。

対応策: 新しい広告ユニットが表示されるまで 1 時間ほどお待ちください。広告ユニットを追加してから 1 時間を超えている場合は次の対応を行います。

広告ユニットコードが正しいことを確認する お支払い情報に未入力の箇所がないか、その他に設定上の問題がないかなど、アカウントのエラーを確認する ポリシー センターでポリシーに関する問題を解決する 注: 無効な操作が原因でアカウントが無効になっている場合、そのアカウントにログインすることはできません。詳細

-> 未設定の本番用IDを指定していまっていたのでtest用IDを使用するように変更

無事実機では表示されました。


emulatorで表示するには?

再度さきほどのエラーログを見返してみて、そのままの意味で受け止める。google play storeがないのが表示できていない理由?

W/GooglePlayServicesUtil(28945): com.example.testtest requires the Google Play Store, but it is missing.

そして私はemulatorにPixel 4aを使っていたがどうやらPixel4aのOSイメージにはgoogle play storeが入っていないらしい。Nexusだとあるらしい。 そして他にもgoogle関連のserviceで必要なのがあるぽいのでSDKManagerでインストールする。

インストール後にADKManagerからemulatorを作成する。 これをgoogle play storeに マークがついてる。

これでok runしてみると、

はい。無事表示されました。 ひっかけだった。

ちなみにemulatorがデフォだとwifi接続できてなかったのでこちらのサイトを参考にして設定した。

[Flutter]Androidエミュレーターが突然インターネット接続できなくなった場合の対処法