앱 오프닝 광고

이 가이드는 Google 모바일 광고 SDK를 사용하여 앱 오프닝 광고를 통합하려는 게시자를 대상으로 작성되었습니다.

앱 오프닝 광고는 앱 로드 화면에서 수익을 올리려는 게시자를 위해 개발된 특별한 광고 형식입니다. 앱 오프닝 광고는 언제든지 닫을 수 있으며 사용자가 앱을 포그라운드로 가져올 때 표시되도록 설계되었습니다.

앱 오프닝 광고는 사용자가 앱을 사용 중임을 알 수 있도록 작은 브랜딩 영역을 자동으로 표시합니다. 앱 오프닝 광고는 다음과 같이 게재됩니다.

기본 요건

항상 테스트 광고로 테스트

앱을 빌드하고 테스트할 때는 운영 중인 실제 광고 대신 테스트 광고를 사용하세요. 이렇게 하지 않으면 계정이 정지될 수 있습니다.

테스트 광고를 로드하는 가장 쉬운 방법은 앱 오프닝 광고 전용 테스트 광고 단위 ID를 사용하는 것입니다.

/21775744923/example/app-open

이 ID는 모든 요청에 대해 테스트 광고를 반환하도록 특별히 구성되었으며, 코딩, 테스트 및 디버깅 중에 앱에서 자유롭게 사용할 수 있습니다. 앱을 게시하기 전에 이 ID를 자체 광고 단위 ID로 바꿔야 합니다.

Google 모바일 광고 SDK의 테스트 광고가 작동하는 방식을 자세히 알아보려면 테스트 광고 사용 설정을 참고하세요.

Application 클래스 확장

Application 클래스를 확장하는 새 클래스를 만들고 다음 코드를 추가하여 앱이 시작될 때 Google 모바일 광고 SDK를 초기화합니다.

Kotlin

/** Application class that initializes, loads and show ads when activities change states. */
class MyApplication : Application() {

  override fun onCreate() {
    super<Application>.onCreate()
    CoroutineScope(Dispatchers.IO).launch {
      // Initialize the Mobile Ads SDK synchronously on a background thread.
      MobileAds.initialize(this@MyApplication, InitializationConfig.Builder(APP_ID).build()) {}
    }
  }

  private companion object {
    // Sample AdMob App ID.
    const val APP_ID = "ca-app-pub-3940256099942544~3347511713"
  }
}

자바

/** Application class that initializes, loads and show ads when activities change states. */
public class MyApplication extends Application {

  // Sample AdMob App ID.
  private static final String APP_ID = "ca-app-pub-3940256099942544~3347511713";

  @Override
  public void onCreate() {
    super.onCreate();
    new Thread(
        () -> {
          // Initialize the Google Mobile Ads SDK on a background thread.
          MobileAds.initialize(
              MyApplication.this,
              new InitializationConfig.Builder(APP_ID).build(),
              initializationStatus -> {});
        })
        .start();
  }
}

이렇게 하면 SDK가 초기화되고 나중에 앱 포그라운드 이벤트에 등록할 수 있는 스켈레톤이 제공됩니다.

다음으로 AndroidManifest.xml에 다음 코드를 추가합니다.

<!-- TODO: Update to reference your actual package name. -->
<application
    android:name="com.google.android.gms.example.appopendemo.MyApplication" ...>
...
</application>

유틸리티 구성요소 구현

광고는 빠르게 게재되어야 하므로 광고를 게재하기 전에 미리 로드하는 것이 좋습니다. 이렇게 하면 사용자가 앱을 실행하자마자 바로 광고를 게재할 수 있습니다.

광고를 표시해야 하는 시점에 앞서 광고 요청을 할 수 있도록 유틸리티 구성요소 AppOpenAdManager를 구현합니다.

Kotlin

/**
* Interface definition for a callback to be invoked when an app open ad is complete (i.e. dismissed
* or fails to show).
*/
fun interface OnShowAdCompleteListener {
  fun onShowAdComplete()
}

/** Singleton object that loads and shows app open ads. */
object AppOpenAdManager {
  private var appOpenAd: AppOpenAd? = null
  private var isLoadingAd = false
  var isShowingAd = false

  /**
  * Load an ad.
  *
  * @param context a context used to perform UI-related operations (e.g. display Toast messages).
  *   Showing the app open ad itself does not require a context.
  */
  fun loadAd(context: Context) {
    // We will implement this later.
  }

  /**
  * Show the ad if one isn't already showing.
  *
  * @param activity the activity that shows the app open ad.
  * @param onShowAdCompleteListener the listener to be notified when an app open ad is complete.
  */
  fun showAdIfAvailable(activity: Activity, onShowAdCompleteListener: OnShowAdCompleteListener?) {
    // We will implement this later.
  }

  /** Check if ad exists and can be shown. */
  private fun isAdAvailable(): Boolean {
    return appOpenAd != null
  }
}

자바

/** Singleton object that loads and shows app open ads. */
public class AppOpenAdManager {

  /**
  * Interface definition for a callback to be invoked when an app open ad is complete (i.e.
  * dismissed or fails to show).
  */
  public interface OnShowAdCompleteListener {
    void onShowAdComplete();
  }

  private static AppOpenAdManager instance;
  private AppOpenAd appOpenAd;
  private boolean isLoadingAd = false;
  private boolean isShowingAd = false;

  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private long loadTime = 0;

  public static synchronized AppOpenAdManager getInstance() {
    if (instance == null) {
      instance = new AppOpenAdManager();
    }
    return instance;
  }

  /**
  * Load an ad.
  *
  * @param context a context used to perform UI-related operations (e.g. display Toast messages).
  *     Loading the app open ad itself does not require a context.
  */
  public void loadAd(@NonNull Context context) {
    // We will implement this later.
  }

  /**
  * Show the ad if one isn't already showing.
  *
  * @param activity the activity that shows the app open ad.
  * @param onShowAdCompleteListener the listener to be notified when an app open ad is complete.
  */
  public void showAdIfAvailable(
      @NonNull Activity activity, @Nullable OnShowAdCompleteListener onShowAdCompleteListener) {
    // We will implement this later.
  }

  /** Check if ad exists and can be shown. */
  private boolean isAdAvailable() {
    return appOpenAd != null
  }
}

광고 로드

다음 단계는 loadAd() 메서드를 작성하고 광고 로드 콜백을 처리하는 것입니다.

Kotlin


/**
 * Load an ad.
 *
 * @param context a context used to perform UI-related operations (e.g. display Toast messages).
 *   Loading the app open ad itself does not require a context.
 */
fun loadAd(context: Context) {
  // Do not load ad if there is an unused ad or one is already loading.
  if (isLoadingAd || isAdAvailable()) {
    Log.d(Constant.TAG, "App open ad is either loading or has already loaded.")
    return
  }

  isLoadingAd = true
  AppOpenAd.load(
    AdRequest.Builder(AppOpenFragment.AD_UNIT_ID).build(),
    object : AdLoadCallback<AppOpenAd> {
      /**
       * Called when an app open ad has loaded.
       *
       * @param ad the loaded app open ad.
       */
      override fun onAdLoaded(ad: AppOpenAd) {
        // Called when an ad has loaded.
        appOpenAd = ad
        isLoadingAd = false
        Log.d(Constant.TAG, "App open ad loaded.")
      }

      /**
       * Called when an app open ad has failed to load.
       *
       * @param loadAdError the error.
       */
      override fun onAdFailedToLoad(loadAdError: LoadAdError) {
        isLoadingAd = false
        Log.w(Constant.TAG, "App open ad failed to load: $loadAdError")
      }
    },
  )
}

자바


/**
 * Load an ad.
 *
 * @param context a context used to perform UI-related operations (e.g. display Toast messages).
 *     Loading the app open ad itself does not require a context.
 */
public void loadAd(@NonNull Context context) {
  // Do not load ad if there is an unused ad or one is already loading.
  if (isLoadingAd || isAdAvailable()) {
    Log.d(Constant.TAG, "App open ad is either loading or has already loaded.");
    return;
  }

  isLoadingAd = true;
  AppOpenAd.load(
      new AdRequest.Builder(AppOpenFragment.AD_UNIT_ID).build(),
      new AdLoadCallback<AppOpenAd>() {
        @Override
        public void onAdLoaded(@NonNull AppOpenAd ad) {
          appOpenAd = ad;
          isLoadingAd = false;
          Log.d(Constant.TAG, "App open ad loaded.");
        }

        @Override
        public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
          isLoadingAd = false;
          Log.w(Constant.TAG, "App open ad failed to load: " + loadAdError);
        }
      });
}

광고 표시 및 전체 화면 콜백 이벤트 처리

가장 일반적인 앱 오프닝 구현은 앱 실행 시에 앱 오프닝 광고를 표시하려고 시도하고, 광고가 준비되지 않은 경우 앱 콘텐츠를 시작하고, 다음 앱 오프닝 기회에 다른 광고를 미리 로드하는 것입니다. 구현 예시는 앱 오프닝 광고 안내를 참고하세요.

다음 코드는 광고를 표시한 후 새로고침하는 방법을 보여줍니다.

Kotlin

/**
 * Show the ad if one isn't already showing.
 *
 * @param activity the activity that shows the app open ad.
 * @param onShowAdCompleteListener the listener to be notified when an app open ad is complete.
 */
fun showAdIfAvailable(activity: Activity, onShowAdCompleteListener: OnShowAdCompleteListener?) {
  // If the app open ad is already showing, do not show the ad again.
  if (isShowingAd) {
    Log.d(Constant.TAG, "App open ad is already showing.")
    onShowAdCompleteListener?.onShowAdComplete()
    return
  }

  // If the app open ad is not available yet, invoke the callback.
  if (!isAdAvailable()) {
    Log.d(Constant.TAG, "App open ad is not ready yet.")
    onShowAdCompleteListener?.onShowAdComplete()
    return
  }

  appOpenAd?.adEventCallback =
    object : AppOpenAdEventCallback {
      override fun onAdShowedFullScreenContent() {
        Log.d(Constant.TAG, "App open ad showed.")
      }

      override fun onAdDismissedFullScreenContent() {
        Log.d(Constant.TAG, "App open ad dismissed.")
        appOpenAd = null
        isShowingAd = false
        onShowAdCompleteListener?.onShowAdComplete()
        loadAd(activity)
      }

      override fun onAdFailedToShowFullScreenContent(
        fullScreenContentError: FullScreenContentError
      ) {
        appOpenAd = null
        isShowingAd = false
        Log.w(Constant.TAG, "App open ad failed to show: $fullScreenContentError")
        onShowAdCompleteListener?.onShowAdComplete()
        loadAd(activity)
      }

      override fun onAdImpression() {
        Log.d(Constant.TAG, "App open ad recorded an impression.")
      }

      override fun onAdClicked() {
        Log.d(Constant.TAG, "App open ad recorded a click.")
      }
    }

  isShowingAd = true
  appOpenAd?.show(activity)
}

자바

/**
 * Show the ad if one isn't already showing.
 *
 * @param activity the activity that shows the app open ad.
 * @param onShowAdCompleteListener the listener to be notified when an app open ad is complete.
 */
public void showAdIfAvailable(
    @NonNull Activity activity, @Nullable OnShowAdCompleteListener onShowAdCompleteListener) {
  // If the app open ad is already showing, do not show the ad again.
  if (isShowingAd) {
    Log.d(Constant.TAG, "App open ad is already showing.");
    if (onShowAdCompleteListener != null) {
      onShowAdCompleteListener.onShowAdComplete();
    }
    return;
  }

  // If the app open ad is not available yet, invoke the callback.
  if (!isAdAvailable()) {
    Log.d(Constant.TAG, "App open ad is not ready yet.");
    if (onShowAdCompleteListener != null) {
      onShowAdCompleteListener.onShowAdComplete();
    }
    return;
  }

  appOpenAd.setAdEventCallback(
      new AppOpenAdEventCallback() {
        @Override
        public void onAdShowedFullScreenContent() {
          Log.d(Constant.TAG, "App open ad shown.");
        }

        @Override
        public void onAdDismissedFullScreenContent() {
          Log.d(Constant.TAG, "App open ad dismissed.");
          appOpenAd = null;
          isShowingAd = false;
          if (onShowAdCompleteListener != null) {
            onShowAdCompleteListener.onShowAdComplete();
          }
          loadAd(activity);
        }

        @Override
        public void onAdFailedToShowFullScreenContent(
            @NonNull FullScreenContentError fullScreenContentError) {
          appOpenAd = null;
          isShowingAd = false;
          Log.w(Constant.TAG, "App open ad failed to show: " + fullScreenContentError);
          if (onShowAdCompleteListener != null) {
            onShowAdCompleteListener.onShowAdComplete();
          }
          loadAd(activity);
        }

        @Override
        public void onAdImpression() {
          Log.d(Constant.TAG, "App open ad recorded an impression.");
        }

        @Override
        public void onAdClicked() {
          Log.d(Constant.TAG, "App open ad recorded a click.");
        }
      });

  isShowingAd = true;
  appOpenAd.show(activity);
}

AppOpenAdEventCallback는 광고가 표시되거나 광고 표시가 실패하거나 광고가 닫히는 등의 이벤트를 처리합니다.

광고 만료 고려

만료된 광고가 표시되지 않도록 하려면 AppOpenAdManager에 메서드를 추가해 광고 참조가 로드된 후 경과한 시간을 확인합니다. 그런 다음 이 메서드를 이용해 광고가 여전히 유효한지 확인하세요.

Kotlin

object AppOpenAdManager {
  // ...
  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private var loadTime: Long = 0;
  
  /**
   * Load an ad.
   *
   * @param context a context used to perform UI-related operations (e.g. display Toast messages).
   *   Loading the app open ad itself does not require a context.
   */
  fun loadAd(context: Context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      Log.d(Constant.TAG, "App open ad is either loading or has already loaded.")
      return
    }

    isLoadingAd = true
    AppOpenAd.load(
      AdRequest.Builder(AppOpenFragment.AD_UNIT_ID).build(),
      object : AdLoadCallback<AppOpenAd> {
        /**
         * Called when an app open ad has loaded.
         *
         * @param ad the loaded app open ad.
         */
        override fun onAdLoaded(ad: AppOpenAd) {
          // Called when an ad has loaded.
          appOpenAd = ad
          isLoadingAd = false
          loadTime = Date().time
          Log.d(Constant.TAG, "App open ad loaded.")
        }

        /**
         * Called when an app open ad has failed to load.
         *
         * @param loadAdError the error.
         */
        override fun onAdFailedToLoad(loadAdError: LoadAdError) {
          isLoadingAd = false
          Log.w(Constant.TAG, "App open ad failed to load: $loadAdError")
        }
      },
    )
  }

  // ...

  /** Check if ad was loaded more than n hours ago. */
  private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean {
    val dateDifference: Long = Date().time - loadTime
    val numMilliSecondsPerHour: Long = 3600000
    return dateDifference < numMilliSecondsPerHour * numHours
  }

  /** Check if ad exists and can be shown. */
  private fun isAdAvailable(): Boolean {
    // App open ads expire after four hours. Ads rendered more than four hours after request time
    // are no longer valid and may not earn revenue.
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4)
  }
}

자바

public class AppOpenAdManager {
  // ...
  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private long loadTime = 0;
  
  /**
   * Load an ad.
   *
   * @param context a context used to perform UI-related operations (e.g. display Toast messages).
   *     Loading the app open ad itself does not require a context.
   */
  public void loadAd(@NonNull Context context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      Log.d(Constant.TAG, "App open ad is either loading or has already loaded.");
      return;
    }

    isLoadingAd = true;
    AppOpenAd.load(
        new AdRequest.Builder(AppOpenFragment.AD_UNIT_ID).build(),
        new AdLoadCallback<AppOpenAd>() {
          @Override
          public void onAdLoaded(@NonNull AppOpenAd ad) {
            appOpenAd = ad;
            isLoadingAd = false;
            loadTime = new Date().getTime();
            Log.d(Constant.TAG, "App open ad loaded.");
          }

          @Override
          public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
            isLoadingAd = false;
            Log.w(Constant.TAG, "App open ad failed to load: " + loadAdError);
          }
        });
  }

  // ...

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

  /** Check if ad exists and can be shown. */
  private boolean isAdAvailable() {
    // App open ads expire after four hours. Ads rendered more than four hours after request time
    // are no longer valid and may not earn revenue.
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
  }
}

현재 활동 추적

광고를 표시하려면 Activity 컨텍스트가 필요합니다. 사용 중인 최신 활동을 추적하려면 Application.ActivityLifecycleCallbacks를 등록하고 구현합니다.

Kotlin

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

  private var currentActivity: Activity? = null

  override fun onCreate() {
    super<Application>.onCreate()
    registerActivityLifecycleCallbacks(this)
  }

  /** ActivityLifecycleCallback methods. */
  override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}

  override fun onActivityStarted(activity: Activity) {
    currentActivity = activity
  }

  override fun onActivityResumed(activity: Activity) {}

  override fun onActivityPaused(activity: Activity) {}

  override fun onActivityStopped(activity: Activity) {}

  override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

  override fun onActivityDestroyed(activity: Activity) {}

  // ...
}

자바

public class MyApplication extends Application
  implements Application.ActivityLifecycleCallbacks {

  private Activity currentActivity;

  @Override
  public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
  }

  /** ActivityLifecycleCallback methods. */
  @Override
  public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {}

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

  @Override
  public void onActivityResumed(@NonNull Activity activity) {}

  @Override
  public void onActivityPaused(@NonNull Activity activity) {}

  @Override
  public void onActivityStopped(@NonNull Activity activity) {}

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

  @Override
  public void onActivityDestroyed(@NonNull Activity activity) {}

  // ...
}

registerActivityLifecycleCallbacks를 사용하면 모든 Activity 이벤트를 수신할 수 있습니다. 활동이 시작되고 소멸될 때를 수신하여 현재 Activity에 대한 참조를 추적할 수 있고, 이 참조는 앱 오프닝 광고를 표시하는 데 사용됩니다.

앱 포그라운드 이벤트 수신

Gradle 파일에 라이브러리 추가

앱 포그라운드 이벤트에 대한 알림을 받으려면 DefaultLifecycleObserver를 등록해야 합니다. 앱 수준 빌드 파일에 종속 항목을 추가합니다.

Kotlin

  dependencies {
    implementation("com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:0.17.0-alpha01")
    implementation("androidx.lifecycle:lifecycle-process:2.8.3")
  }

Groovy

  dependencies {
    implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:0.17.0-alpha01'
    implementation 'androidx.lifecycle:lifecycle-process:2.8.3'
  }

수명 주기 관찰자 인터페이스 구현

DefaultLifecycleObserver 인터페이스를 구현하여 포그라운드 이벤트를 수신할 수 있습니다.

onStart 이벤트를 구현하여 앱 오프닝 광고를 표시합니다.

Kotlin

class MyApplication :
  Application(), Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver {

  private var currentActivity: Activity? = null

  override fun onCreate() {
    super<Application>.onCreate()
    registerActivityLifecycleCallbacks(this)
    ProcessLifecycleOwner.get().lifecycle.addObserver(this)
  }

  /**
  * DefaultLifecycleObserver method that shows the app open ad when the app moves to foreground.
  */
  override fun onStart(owner: LifecycleOwner) {
    currentActivity?.let { activity ->
      AppOpenAdManager.showAdIfAvailable(activity, null)
    }
  }

  // ...
}

자바

public class MyApplication extends Application
  implements Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver {

  private Activity currentActivity;

  @Override
  public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
    ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
  }

  /**
  * DefaultLifecycleObserver method that shows the app open ad when the app moves to foreground.
  */
  @Override
  public void onStart(@NonNull LifecycleOwner owner) {
    if (currentActivity == null) {
      return;
    }

    AppOpenAdManager.getInstance().showAdIfAvailable(currentActivity, null);
  }

  // ...
}

콜드 스타트 및 로드 화면

지금까지 이 문서에서는 메모리에 일시중지된 상태로 있는 앱을 사용자가 포그라운드로 가져올 때만 앱 오프닝 광고가 게재된다고 가정했습니다. '콜드 스타트'는 앱이 실행되었지만 실행되기 전에 앱이 일시중지 상태로 메모리에 있지 않은 경우 발생합니다.

콜드 스타트의 한 예는 사용자가 앱을 처음으로 열 때입니다. 콜드 스타트 상황에서는 즉시 표시할 수 있도록 미리 로드해둔 앱 오프닝 광고가 없습니다. 광고를 요청하고 광고를 수신하는 시점 사이에 지연 시간이 발생하면 사용자가 그 잠깐 동안 앱을 사용하다가 갑작스럽게 광고가 게재되며 불편을 겪을 수 있습니다. 이는 사용자 경험에 악영향을 미치므로 피해야 합니다.

콜드 스타트가 발생할 때 앱 오프닝 광고를 사용하는 바람직한 방법은 게임 또는 앱 애셋을 로드하기 위한 로드 화면을 사용하고 해당 로드 화면에만 광고를 표시하는 것입니다. 앱에서 로드가 완료되고 사용자에게 앱의 주요 콘텐츠가 표시된 후에는 광고가 게재되지 않아야 합니다.

권장사항

앱 오프닝 광고를 사용하면 앱을 처음 실행할 때와 앱을 전환하는 동안 앱의 로드 화면으로 수익을 창출할 수 있지만, 사용자가 앱을 사용하는 데 불편하지 않도록 권장사항을 고려하는 것이 중요합니다. 이러한 권장사항은 다음과 같습니다.

  • 사용자가 앱을 몇 번 사용한 후에 첫 앱 오프닝 광고를 게재하세요.
  • 사용자가 앱이 로드되기를 기다리는 동안 앱 오프닝 광고를 게재하세요.
  • 앱 오프닝 광고 아래에 로드 화면이 있고 광고가 닫히기 전에 화면 로드가 완료되면 onAdDismissedFullScreenContent() 메서드로 로드 화면을 닫는 것이 좋습니다.

차세대 Google 모바일 광고 SDK 사용을 보여주는 예시 앱을 다운로드하여 실행합니다.