App Open Ads

本指南面向希望使用 Google 移动广告 Android SDK 集成开屏广告的发布商。

开屏广告是一种特殊的广告格式,适合希望通过应用加载屏幕获利的发布商。开屏广告在用户将您的应用切换为在前台运行时展示,用户可随时关闭。

开屏广告会自动显示一个较小的区域并在其中展示品牌信息,让用户知道他们在使用您的应用。以下是一个开屏广告示例:

概括来讲,需要执行的重要步骤包括:

  1. 扩展 Application 类以初始化 GMA SDK。
  2. 创建实用工具类,用于先加载广告,以备需要展示时使用。
  3. 加载广告。
  4. 监听 ActivityLifecycleCallbacks.
  5. 展示广告并处理回调。
  6. 实现并注册 LifecycleObserver 接口,以便在出现前台事件期间展示广告。

前提条件

  • Google 移动广告 SDK 19.4.0 或更高版本。
  • 按照入门指南中的设置说明操作。

务必用测试广告进行测试

在构建和测试应用时,请确保使用的是测试广告,而不是实际投放的广告。否则,可能会导致您的帐号被暂停。

对于开屏广告,加载测试广告最简便的方法就是使用下面的测试专用广告单元 ID:

/6499/example/app_open_new

该测试广告单元 ID 已经过专门配置,可确保每个请求返回的都是测试广告。您可以在自己应用的编码、测试和调试过程中随意使用该测试广告单元 ID。只需确保您会在发布应用前用自己的广告单元 ID 替换该测试广告单元 ID 即可。

如需详细了解移动广告 SDK 的测试广告如何运作,请参阅测试广告

扩展 Application 类

创建名为 MyApplication 的新类并添加以下代码:

package com.google.android.gms.example.appopendemo;

import android.app.Application;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;

/** The Application class that manages AppOpenManager. */
public class MyApplication extends Application {

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

这将初始化 SDK 并提供一个框架,供您稍后在其中注册应用前台事件。

接下来,将以下代码添加到您的 AndroidManifest.xml 中:

<application
    android:name="com.google.android.gms.example.appopendemo.MyApplication" ...>
...
</application>

请务必引用您的实际软件包名称。

实现实用工具类

您的广告应该要快速展示,因此最好先加载广告,以备需要展示时使用。这样一来,用户进入您的应用后,广告便可以立即展示。实现实用工具类,即可在您需要展示广告之前发出广告请求。

创建名为 AppOpenManager 的新类并在其中添加相关代码,如下所示:

import static androidx.lifecycle.Lifecycle.Event.ON_START;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.appopen.AppOpenAd;
import java.util.Date;

/** Prefetches App Open Ads. */
public class AppOpenManager {
  private static final String LOG_TAG = "AppOpenManager";
  private static final String AD_UNIT_ID = "ca-app-pub-3940256099942544/3419835294";
  private AppOpenAd appOpenAd = null;

  private AppOpenAd.AppOpenAdLoadCallback loadCallback;

  private final MyApplication myApplication;

  /** Constructor */
  public AppOpenManager(MyApplication myApplication) {
    this.myApplication = myApplication;
  }

  /** Request an ad */
  public void fetchAd() {
    // We will implement this below.
  }

  /** Creates and returns ad request. */
  private AdRequest getAdRequest() {
    return new AdRequest.Builder().build();
  }

  /** Utility method that checks if ad exists and can be shown. */
  public boolean isAdAvailable() {
    return appOpenAd != null;
  }
}

此类 shell 管理实例变量,以便跟踪加载的广告、广告单元 ID 和 AppOpenAdLoadCallback

现在您已拥有实用工具类,可以在 MyApplication 类中将其实例化:

/** The Application class that manages AppOpenManager. */
public class MyApplication extends Application {

  private static AppOpenManager appOpenManager;

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

    appOpenManager = new AppOpenManager(this);

  }
}

加载广告

下一步是填充 fetchAd() 方法。请将以下代码添加到 AppOpenManager 类中:

/** Prefetches App Open Ads and handles lifecycle detection. */
public class AppOpenManager {
  ...
  /** Request an ad */
  public void fetchAd() {
    // Have unused ad, no need to fetch another.
    if (isAdAvailable()) {
      return;
    }

    loadCallback =
        new AppOpenAd.AppOpenAdLoadCallback() {
          /**
           * Called when an app open ad has loaded.
           *
           * @param ad the loaded app open ad.
           */
          @Override
          public void onAdLoaded(AppOpenAd ad) {
            AppOpenManager.this.appOpenAd = ad;
          }

          /**
           * Called when an app open ad has failed to load.
           *
           * @param loadAdError the error.
           */
          @Override
          public void onAdFailedToLoad(LoadAdError loadAdError) {
            // Handle the error.
          }

        };
    AdRequest request = getAdRequest();
    AppOpenAd.load(
        myApplication, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, loadCallback);
  }
  ...
}

AppOpenAdLoadCallback 包含 AppOpenAd 加载完毕时调用的方法。

跟踪当前 Activity

您需要 Activity 上下文才能展示广告。要跟踪用户使用的最新 Activity,需要让 AppOpenManager 类实现 Application.ActivityLifecycleCallbacks 接口:

...
/** Prefetches App Open Ads and handles lifecycle detection. */
public class AppOpenManager implements Application.ActivityLifecycleCallbacks {

  private Activity currentActivity;

  ...

  /** ActivityLifecycleCallback methods */
  @Override
  public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}

  @Override
  public void onActivityStarted(Activity activity) {
    currentActivity = activity;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    currentActivity = activity;
  }

  @Override
  public void onActivityStopped(Activity activity) {}

  @Override
  public void onActivityPaused(Activity activity) {}

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}

  @Override
  public void onActivityDestroyed(Activity activity) {
    currentActivity = null;
  }
}

通过跟踪当前 Activity,您可以获得用于展示广告的上下文。现在,您需要使用 AppOpenManager 构造函数中的 registerActivityLifecycleCallbacks Application 方法注册此接口。

/** Constructor */
public AppOpenManager(MyApplication myApplication) {
  this.myApplication = myApplication;
  this.myApplication.registerActivityLifecycleCallbacks(this);
}

registerActivityLifecycleCallbacks 可用于监听所有 Activity 事件。通过监听 Activity 启动和销毁的时间,您可以跟踪对当前 Activity 的引用,然后根据该 Activity 展示您的开屏广告。

展示广告并处理全屏回调事件

将以下代码添加到 AppOpenManager 类中:

/** Prefetches App Open Ads and handles lifecycle detection. */
public class AppOpenManager implements Application.ActivityLifecycleCallbacks {
  ...
  private static boolean isShowingAd = false;

  /** Shows the ad if one isn't already showing. */
  public void showAdIfAvailable() {
    // Only show ad if there is not already an app open ad currently showing
    // and an ad is available.
    if (!isShowingAd && isAdAvailable()) {
      Log.d(LOG_TAG, "Will show ad.");

      FullScreenContentCallback fullScreenContentCallback =
          new FullScreenContentCallback() {
            @Override
            public void onAdDismissedFullScreenContent() {
              // Set the reference to null so isAdAvailable() returns false.
              AppOpenManager.this.appOpenAd = null;
              isShowingAd = false;
              fetchAd();
            }

            @Override
            public void onAdFailedToShowFullScreenContent(AdError adError) {}

            @Override
            public void onAdShowedFullScreenContent() {
              isShowingAd = true;
            }
          };

      appOpenAd.setFullScreenContentCallback(fullScreenContentCallback);
      appOpenAd.show(currentActivity);

    } else {
      Log.d(LOG_TAG, "Can not show ad.");
      fetchAd();
    }
  }
  ...
}

此方法展示广告,并传入 FullScreenContentCallback 匿名类,用于处理各种事件,例如广告展示、广告无法展示或者用户关闭广告。如果用户在离开您的应用后,通过点击开屏广告返回到您的应用,此方法可确保不会向用户展示其他开屏广告。

监听应用前台事件

将库添加到 gradle 文件中

要接收应用前台事件的通知,您需要注册 LifecycleObserver。首先,修改应用级的 build.gradle 文件,在其中包含 LifecycleObserver 库:

apply plugin: 'com.android.application'

android {
   compileSdkVersion 28
   buildToolsVersion "29.0.0"
   defaultConfig {
       applicationId "com.google.android.gms.example.appopendemo"
       minSdkVersion 18
       targetSdkVersion 28
       versionCode 1
       versionName "1.0"
       testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }
   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
}

dependencies {
   implementation 'androidx.appcompat:appcompat:1.0.2'
   implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

   implementation 'com.google.android.gms:play-services-ads:19.4.0'

   def lifecycle_version = "2.0.0"
   implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
   implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
   annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
}

实现 LifecycleObserver 接口

您可以让 AppOpenManager 类实现 LifecycleObserver 接口,以便监听前台事件。您可以通过添加以下代码来修改类:

package com.google.android.gms.example.appopendemo;

import static androidx.lifecycle.Lifecycle.Event.ON_START;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.appopen.AppOpenAd;
import java.util.Date;

/** Prefetches App Open Ads and handles lifecycle detection. */
public class AppOpenManager implements LifecycleObserver, Application.ActivityLifecycleCallbacks {
  ...
  /** Constructor */
  public AppOpenManager(MyApplication myApplication) {
    this.myApplication = myApplication;
    this.myApplication.registerActivityLifecycleCallbacks(this);
    ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
  }

  /** LifecycleObserver methods */
  @OnLifecycleEvent(ON_START)
  public void onStart() {
    showAdIfAvailable();
    Log.d(LOG_TAG, "onStart");
  }
  ...
}

注册 LifecycleObserver 后,您的应用会收到有关应用启动和前台事件的提醒,并且能够适时展示广告。

考虑广告有效期

为确保您不会展示过期的广告,请在 AppOpenManager 中添加一个方法,用于检查广告引用加载后经过了多长时间。然后,使用该方法检查广告是否仍然有效。按如下方式更新 AppOpenManager 方法:

/** Prefetches App Open Ads and handles lifecycle detection. */
public class AppOpenManager implements LifecycleObserver, Application.ActivityLifecycleCallbacks {
  ...
  private long loadTime = 0;

  /** Request an ad */
  public void fetchAd() {
    // Have unused ad, no need to fetch another.
    if (isAdAvailable()) {
      return;
    }

    loadCallback =
        new AppOpenAd.AppOpenAdLoadCallback() {
          /**
           * Called when an app open ad has loaded.
           *
           * @param ad the loaded app open ad.
           */
          @Override
          public void onAppOpenAdLoaded(AppOpenAd ad) {
            AppOpenManager.this.appOpenAd = ad;
            AppOpenManager.this.loadTime = (new Date()).getTime();
          }

          /**
           * Called when an app open ad has failed to load.
           *
           * @param loadAdError the error.
           */
          @Override
          public void onAppOpenAdFailedToLoad(LoadAdError loadAdError) {
            // Handle the error.
          }

        };
    AdRequest request = getAdRequest();
    AppOpenAd.load(
        myApplication, AD_UNIT_ID, request, AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, loadCallback);
  }
  ...

  /** Utility method to check if ad was loaded more than n hours ago. */
  private boolean wasLoadTimeLessThanNHoursAgo(long numHours) {
    long dateDifference = (new Date()).getTime() - this.loadTime;
    long numMilliSecondsPerHour = 3600000;
    return (dateDifference < (numMilliSecondsPerHour * numHours));
  }

  /** Utility method that checks if ad exists and can be shown. */
  public boolean isAdAvailable() {
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
  }
}

冷启动和加载屏幕

到现在为止,本文档都假定您仅在以下情况下展示开屏广告:用户将在内存中挂起的应用切换为在前台运行。用户启动您的应用,但该应用之前未在内存中挂起,这种情况就称为“冷启动”。

例如,用户首次打开您的应用便属于冷启动。对于冷启动,您没有之前已加载的开屏广告可供立即展示。请求广告和收到相应广告之间的延迟会导致出现以下情况:用户能够暂时使用您的应用,然后突然看到一条无关广告。应避免出现这种情况,因为这会导致用户体验不佳。

在冷启动时使用开屏广告的首选方法是,使用加载屏幕来加载游戏或应用素材资源,并且仅在加载屏幕展示广告。如果您的应用已加载完毕,并且用户已经访问应用的主要内容,则不要展示广告。

最佳做法

借助开屏广告,您可以在用户首次启动应用和切换应用期间通过应用的加载屏幕获利,不过,还请务必考虑一些最佳做法,以便用户喜欢使用您的应用。最佳做法如下所示:

  • 在用户使用几次您的应用后展示第一个开屏广告。
  • 在用户等待您的应用加载时展示开屏广告。
  • 如果有加载屏幕位于开屏广告之下,并且加载屏幕在用户关闭广告之前已加载完毕,您可能需要通过 onAdDismissedFullScreenContent() 方法关闭加载屏幕。