Google Admob广告接入

1 Admob介绍

Google AdMob是目前全球最大的移动APP广告平台,向应用程序开发者以及需要在应用程序中进行广告投放的广告主开放,为在移动端上发掘商机、塑造品牌和实现收益提供解决方案。目前AdMob已覆盖200多个国家和地区,超9亿移动设备数。目前大概拥有全球百万广告主,近两年为开发者带来超过10亿美金的收入。最近项目上开展海外APP投放,需要对接Google Admob广告。关于接入方式,网上的文章可以参考。但是都不详细,借鉴意义有限。因此自己便从头到尾记录一下整个广告的接入和调试。

2 Admob接入和初始化

2.1 申请app和ad unit

登录google admob添加app,然后在app中申请广告单元即可。

添加app.jpg

图1  admob中添加app

2.2 admob初始化

初始化之前请先添加admob中的相关依赖。另外初始化分为两部分,第一在manifest中添加metadata,填入上一步中申请得到的appid。然后在代码中调用sdk初始化函数initialize。

  • 添加依赖

    修改app/build.gradle,添加play-services-ads依赖。

  • manifest

    <meta-data
        android:name="com.google.android.gms.ads.APPLICATION_ID"
        android:value="ca-app-pub-502545786537xxxx~590565xxxx" />
    
  • sdk初始化

    直接在Application中或者Splash Activity中初始化admob sdk。

    MobileAds.initialize(this, new OnInitializationCompleteListener() {
            @Override
            public void onInitializationComplete(InitializationStatus initializationStatus) {
            }
        });
    

3 Admob广告类型

3.1 横幅广告(banner ads)

横幅广告是一种矩形广告,会固定占据应用中的一部分版面,可在一段时间后自动刷新。横幅广告通常使用FrameLayout搭配AdView实现,先在布局中添加一个FrameLayout,然后在代码中构建AdView并将其添加到FrameLayout中。

3.1.1 横幅广告接入

<FrameLayout
    android:id="@+id/ad_view_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:layout_alignParentBottom="true" />

代码中完成Adview的初始化和广告加载。

// adViewContainer\344\270\272FrameLayout
adView = new AdView(this);
adView.setAdUnitId(Const.getBannerAdId());
adViewContainer.addView(adView);
AdRequest adRequest =
    new AdRequest.Builder().build();
adView.setAdSize(AdSize.BANNER);
adView.loadAd(adRequest);

通常横幅广告包含三种类型,第一普通的横幅广告,第二智能横幅广告,第三自适应横幅广告。上面的例子演示的是普通的横幅广告,其实三者唯一的区别就是调用setAdSize所传入的参数不同。普通横幅广告使用固定宽高,当setAdSize的参数为SMART_BANNER的时候则为智能横幅广告,此时会获取屏幕宽度再加上固定的高度进行呈现。

最后一种就是自适应横幅广告。自适应横幅广告旨在暂时取代符合行业标准的320x50横幅广告尺寸及智能横幅广告格式。自适应横幅广告可更好地利用可用的屏幕尺寸,会为特定设备选择最优高度,而非针对不同尺寸的设备均使用固定高度,从而降低了设备屏幕尺寸多样造成的影响。可以使用三个方法来获取自适应横幅广告尺寸:一种适用于横向屏幕,一种适用于纵向屏幕,还有一种适用于执行操作时的屏幕方向。参考代码如下所示

private AdSize getAdSize() {
    // Step 2 - Determine the screen width (less decorations) to use for the ad width.
    Display display = getWindowManager().getDefaultDisplay();
    DisplayMetrics outMetrics = new DisplayMetrics();
    display.getMetrics(outMetrics);

    float widthPixels = outMetrics.widthPixels;
    float density = outMetrics.density;

    int adWidth = (int) (widthPixels / density);

    // Step 3 - Get adaptive ad size and return for setting on the ad view.
    return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth);
}

3.1.2 横幅广告回调

横幅广告使用AdListener来监听广告事件,比如广告加载、打开和关闭。

mAdView.setAdListener(new AdListener() {
        @Override
        public void onAdLoaded() {
            // Code to be executed when an ad finishes loading.
        }

        @Override
        public void onAdFailedToLoad(LoadAdError adError) {
            // Code to be executed when an ad request fails.
        }

        @Override
        public void onAdOpened() {
            // Code to be executed when an ad opens an overlay that
            // covers the screen.
        }

        @Override
        public void onAdClicked() {
            // Code to be executed when the user clicks on an ad.
        }

        @Override
        public void onAdClosed() {
            // Code to be executed when the user is about to return
            // to the app after tapping on an ad.
        }
    });

3.2 插页广告(interstitial ads)

插页广告一种全屏广告格式,可以在自然间歇点和过渡点(比如在Activity切换,或者完成特定关卡时)显示。插页广告不需要添加任何布局,只需要在插页广告load成功之后,调用show函数进行展示即可。

3.2.1 插页广告接入

插页广告不同于横幅广告,横幅广告只需要调用load即可完成加载和展示两个操作。插页广告只是把这两个动作,单独开来,需要先load,当load成功之后会将构造好的InterstitialAd在onAdLoaded回调中传递回来,然后调用InterstitialAd的show方法即可展示。

AdRequest adRequest = new AdRequest.Builder().build();

InterstitialAd.load(this,"ca-app-pub-3940256099942544/1033173712", adRequest,
                    new InterstitialAdLoadCallback() {
                        @Override
                        public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
                            // The mInterstitialAd reference will be null until
                            // an ad is loaded.
                            mInterstitialAd = interstitialAd;
                            Log.i(TAG, "onAdLoaded");
                        }

                        @Override
                        public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
                            // Handle the error
                            Log.i(TAG, loadAdError.getMessage());
                            mInterstitialAd = null;
                        }
                    });

3.2.2 插页广告回调

插页广告的回调有两个,一个是在load过程中的回调,可以监测load是否成功,还有一个FullScreenContentCallback用来处理与展示InterstitialAd相关的事件。

mInterstitialAd.setFullScreenContentCallback(new FullScreenContentCallback(){
        @Override
        public void onAdDismissedFullScreenContent() {
            // Called when fullscreen content is dismissed.
            Log.d("TAG", "The ad was dismissed.");
        }

        @Override
        public void onAdFailedToShowFullScreenContent(AdError adError) {
            // Called when fullscreen content failed to show.
            Log.d("TAG", "The ad failed to show.");
        }

        @Override
        public void onAdShowedFullScreenContent() {
            // Called when fullscreen content is shown.
            // Make sure to set your reference to null so you don't
            // show it a second time.
            mInterstitialAd = null;
            Log.d("TAG", "The ad was shown.");
        }
    });

3.3 原生广告(native ads)

原始广告是最自由的一种形式。可自定义的广告格式,其外观和风格与您的应用浑然一体,这种广告内嵌于应用内容中显示,需要自己设计布局。

3.3.1 原生广告接入

原生广告的对接最为负责,Google Admob Sdk只返回NativeAd对象,需要我们自己将NativeAd对象中的内容渲染到提前设计好的布局(NativeAdView)中。

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
                                                  xmlns:app="http://schemas.android.com/apk/res-auto"
                                                  android:layout_width="fill_parent"
                                                  android:layout_height="wrap_content">

  <androidx.constraintlayout.widget.ConstraintLayout
      android:layout_width="280dip"
      android:layout_height="240dip"
      android:layout_gravity="center"
      android:background="@drawable/shape_bg_ad_white_12"
      android:clipChildren="true">

    <com.google.android.gms.ads.nativead.MediaView
        android:id="@+id/nativeMedia"
        android:layout_width="fill_parent"
        android:layout_height="187.0dip"
        android:layout_gravity="center_horizontal"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:layout_width="23.0dip"
        android:layout_height="wrap_content"
        android:layout_alignEnd="@id/nativeName"
        android:layout_centerVertical="true"
        android:layout_marginStart="10.0dip"
        android:background="@drawable/shape_tag_ad_corner_3"
        android:gravity="center"
        android:text="AD"
        android:textColor="@color/white"
        android:textSize="10.0sp"
        app:layout_constraintBottom_toBottomOf="@id/nativeMedia"
        app:layout_constraintEnd_toEndOf="parent" />

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/nativeIcon"
        android:layout_width="39.0dip"
        android:layout_height="39.0dip"
        android:layout_marginStart="12.0dip"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/nativeMedia" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/nativeName"
        android:layout_width="0.0dip"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="12.0dip"
        android:layout_marginLeft="12.0dip"
        android:layout_marginRight="12.0dip"
        android:ellipsize="end"
        android:singleLine="true"
        android:textColor="@color/material_blue_grey_500"
        android:textSize="14.0sp"
        app:layout_constraintEnd_toStartOf="@id/nativeBtn"
        app:layout_constraintStart_toEndOf="@id/nativeIcon"
        app:layout_constraintTop_toTopOf="@id/nativeIcon" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/nativeHint"
        android:layout_width="0.0dip"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="12.0dip"
        android:layout_marginLeft="12.0dip"
        android:layout_marginTop="2.0dip"
        android:layout_marginRight="12.0dip"
        android:ellipsize="end"
        android:singleLine="true"
        android:textColor="#ffb6b6b6"
        android:textSize="12.0sp"
        app:layout_constraintEnd_toStartOf="@id/nativeBtn"
        app:layout_constraintStart_toEndOf="@id/nativeIcon"
        app:layout_constraintTop_toBottomOf="@id/nativeName" />

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/nativeBtn"
        style="@style/Widget.AppCompat.Button.Borderless"
        android:layout_width="78.0dip"
        android:layout_height="34.0dip"
        android:layout_marginEnd="12.0dip"
        android:background="@drawable/selector_btn_ad"
        android:gravity="center"
        android:textAllCaps="false"
        android:textColor="@color/white"
        android:textSize="13.0sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/nativeMedia" />
  </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.gms.ads.nativead.NativeAdView>
AdLoader.Builder
            builder =
            new AdLoader.Builder(context, id)
                .forNativeAd(listener);
        VideoOptions videoOptions =
            new VideoOptions.Builder().setStartMuted(false).build();
        NativeAdOptions adOptions =
            new NativeAdOptions.Builder().setVideoOptions(videoOptions)
                .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_TOP_LEFT).build();
        builder.withNativeAdOptions(adOptions);
        // adListener\345\217\257\344\273\245\345\260\206native\347\232\204\345\212\240\350\275\275\347\273\223\346\236\234\350\277\233\350\241\214\345\233\236\344\274\240
        AdLoader adLoader =
            builder
                .withAdListener(adListener)
                .build();
        adLoader.loadAd(new AdRequest.Builder().setHttpTimeoutMillis(30000).build());

在adListener中的onNativeAdLoaded完成界面渲染和展示。

FrameLayout frameLayout =
    findViewById(R.id.fl_adplaceholder);
// Assumes that your ad layout is in a file call ad_unified.xml
// in the res/layout folder
NativeAdView adView = (NativeAdView) getLayoutInflater()
    .inflate(R.layout.ad_unified, null);
// This method sets the text, images and the native ad, etc into the ad
// view.
displayNativeAd(NativeAd, adView);
frameLayout.removeAllViews();
frameLayout.addView(adView);

private void displayNativeAd(ViewGroup parent, NativeAd ad) {

    // Inflate a layout and add it to the parent ViewGroup.
    LayoutInflater inflater = (LayoutInflater) parent.getContext()
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    NativeAdView adView = (NativeAdView) inflater
        .inflate(R.layout.my_ad_layout, parent);

    // Locate the view that will hold the headline, set its text, and call the
    // NativeAdView's setHeadlineView method to register it.
    TextView headlineView = adView.findViewById<TextView>(R.id.ad_headline);
    headlineView.setText(ad.getHeadline());
    adView.setHeadlineView(headlineView);

    ...
        // Repeat the above process for the other assets in the NativeAd
        // using additional view objects (Buttons, ImageViews, etc).
        ...

        // If the app is using a MediaView, it should be
        // instantiated and passed to setMediaView. This view is a little different
        // in that the asset is populated automatically, so there's one less step.
        MediaView mediaView = (MediaView) adView.findViewById(R.id.ad_media);
        adView.setMediaView(mediaView);

        // Call the NativeAdView's setNativeAd method to register the
        // NativeAdObject.
        adView.setNativeAd(ad);

        // Ensure that the parent view doesn't already contain an ad view.
        parent.removeAllViews();

        // Place the AdView into the parent.
        parent.addView(adView);
}

3.3.2 原生广告回调

原生广告使用OnNativeAdLoadedListener返回加载状态。使用和横幅广告一样的回调AdListener来监听广告事件。

3.4 开屏广告(start ads)

开屏广告只是特殊的插页广告,流程和插页广告一致,回调方法也一致。只是使用的类型不一样,开屏广告使用AppOpenAd。

3.5 激励广告(rewarded ads)

激励广告中还有插页式激励广告,这两种在我自己开发的工具类应用不会使用到,这里不做介绍。

4 测试

4.1 测试广告单元和测试设备

广告接入完成之后自然是需要进行测试,验证广告是否接入成功。这里官方提供了两种方法。

  • 使用测试广告单元 adtest_unit.png
  • 以编程方式添加测试设备

    广告接入成功之后,检查logcat输出,以查找像下面这样的消息(向您显示您的设备 ID 以及如何将设备添加为测试设备)。

    I/Ads: Use RequestConfiguration.Builder.setTestDeviceIds(Arrays.asList("33BE2250B43518CCDA7DE426D04EE231")) to get test ads on this device."

    然后修改代码,以便调用 RequestConfiguration.Builder.setTestDeviceIds()并将其传入您的测试设备ID列表。

    List<String> testDeviceIds = Arrays.asList("33BE2250B43518CCDA7DE426D04EE231");
    RequestConfiguration configuration =
        new RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build();
    MobileAds.setRequestConfiguration(configuration);
    

4.2 广告检查器

另外还可以使用特殊方法启动广告检查器。启动后,系统会显示广告检查器主屏幕,列出AdMob界面中当前AdMob应用ID相关联的所有具有出价或中介配置的广告单元。 adinspector.png