이 가이드는 Google 모바일 광고 Android SDK를 사용하여 앱 오프닝 광고를 통합하는 게시자를 대상으로 합니다.
앱 오프닝 광고는 앱 로드 화면을 통해 수익을 창출하려는 게시자를 위한 특별한 광고 형식입니다. 앱 오프닝 광고는 언제든지 닫을 수 있으며 사용자가 앱을 포그라운드로 가져올 때 표시되도록 설계되었습니다.
앱 오프닝 광고는 사용자가 앱을 사용 중임을 알 수 있도록 작은 브랜딩 영역을 자동으로 표시합니다. 앱 오프닝 광고는 다음과 같이 게재됩니다.
개략적으로 다음은 중요한 단계입니다.
Application
클래스를 확장하여 Google 모바일 광고 SDK를 초기화합니다.- 광고를 표시하기 전에 로드하는 유틸리티 클래스를 만듭니다.
- 광고를 로드합니다.
ActivityLifecycleCallbacks.
듣기- 광고를 표시하고 콜백을 처리합니다.
- 포그라운드 이벤트 중에 광고를 표시하도록
LifecycleObserver
인터페이스를 구현하고 등록합니다.
기본 요건
- Google 모바일 광고 SDK 19.4.0 이상
- 시작 가이드의 설정 안내를 따릅니다.
항상 테스트 광고로 테스트
앱을 빌드하고 테스트할 때는 실제 프로덕션 광고 대신 테스트 광고를 사용하세요. 이렇게 하지 않으면 계정이 정지될 수 있습니다.
테스트 광고를 로드하는 가장 쉬운 방법은 앱 오프닝 광고를 위한 테스트 전용 광고 단위 ID를 사용하는 것입니다.
/6499/example/app-open
이 ID는 모든 요청에 대해 테스트 광고를 반환하도록 특별히 구성되었으며, 코딩, 테스트, 디버깅 중에 앱에서 자유롭게 사용할 수 있습니다. 앱을 게시하기 전에 이 ID를 자체 광고 단위 ID로 바꿔야 합니다.
모바일 광고 SDK의 테스트 광고가 작동하는 방식을 자세히 알아보려면 테스트 광고를 참고하세요.
Application 클래스 확장
Application
클래스를 확장하는 새 클래스를 만들고 다음 코드를 추가하여
Google 모바일 광고 SDK를 초기화합니다.
Java
/** Application class that initializes, loads and show ads when activities change states. */
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
new Thread(
() -> {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this, initializationStatus -> {});
})
.start();
}
}
Kotlin
/** Application class that initializes, loads and show ads when activities change states. */
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val backgroundScope = CoroutineScope(Dispatchers.IO)
backgroundScope.launch {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this@MyApplication) {}
}
}
}
이렇게 하면 SDK가 초기화되고 나중에 앱 포그라운드 이벤트에 등록할 스켈레톤이 제공됩니다.
다음으로, AndroidManifest.xml
에 다음 코드를 추가합니다.
<application
android:name="com.google.android.gms.example.appopendemo.MyApplication" ...>
...
</application>
실제 패키지 이름을 참조해야 합니다.
유틸리티 클래스 구현
광고는 빠르게 게재되어야 하므로 광고를 게재하기 전에 로드하는 것이 좋습니다. 이렇게 하면 사용자가 앱을 실행하자마자 바로 광고를 준비할 수 있습니다. 유틸리티 클래스를 구현하여 광고를 표시해야 하는 시점에 앞서 광고를 요청할 수 있습니다.
MyApplication
클래스 내에 AppOpenAdManager
라는 새 클래스를 만들고 다음과 같이 작성합니다.
Java
public class MyApplication extends Application {
...
/** Inner class that loads and shows app open ads. */
private class AppOpenAdManager {
private static final String LOG_TAG = "AppOpenAdManager";
private static final String AD_UNIT_ID = "ca-app-pub-3940256099942544/9257395921";
private AppOpenAd appOpenAd = null;
private boolean isLoadingAd = false;
private boolean isShowingAd = false;
/** Constructor. */
public AppOpenAdManager() {}
/** Request an ad. */
private void loadAd(Context context) {
// We will implement this below.
}
/** Check if ad exists and can be shown. */
private boolean isAdAvailable() {
return appOpenAd != null;
}
}
}
Kotlin
private const val String LOG_TAG = "AppOpenAdManager"
private const val String AD_UNIT_ID = "ca-app-pub-3940256099942544/9257395921"
public class MyApplication extends Application {
...
/** Inner class that loads and shows app open ads. */
private inner class AppOpenAdManager {
private var appOpenAd: AppOpenAd? = null
private var isLoadingAd = false
var isShowingAd = false
/** Request an ad. */
fun loadAd(context: Context) {
// We will implement this below.
}
/** Check if ad exists and can be shown. */
private fun isAdAvailable(): Boolean {
return appOpenAd != null
}
}
}
이제 유틸리티 클래스가 있으므로 MyApplication
클래스에서 인스턴스화할 수 있습니다.
Java
public class MyApplication extends Application {
private AppOpenAdManager appOpenAdManager;
@Override
public void onCreate() {
super.onCreate();
new Thread(
() -> {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this, initializationStatus -> {});
})
.start();
appOpenAdManager = new AppOpenAdManager(this);
}
}
Kotlin
class MyApplication : Application() {
private lateinit var appOpenAdManager: AppOpenAdManager
override fun onCreate() {
super.onCreate()
val backgroundScope = CoroutineScope(Dispatchers.IO)
backgroundScope.launch {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this@MyApplication) {}
}
appOpenAdManager = AppOpenAdManager()
}
}
광고 로드
다음 단계는 loadAd()
메서드를 작성하는 것입니다.
Java
private class AppOpenAdManager {
...
/** Request an ad. */
public void loadAd(Context context) {
// Do not load ad if there is an unused ad or one is already loading.
if (isLoadingAd || isAdAvailable()) {
return;
}
isLoadingAd = true;
AdRequest request = new AdRequest.Builder().build();
AppOpenAd.load(
context, AD_UNIT_ID, request,
AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
new AppOpenAdLoadCallback() {
@Override
public void onAdLoaded(AppOpenAd ad) {
// Called when an app open ad has loaded.
Log.d(LOG_TAG, "Ad was loaded.");
appOpenAd = ad;
isLoadingAd = false;
}
@Override
public void onAdFailedToLoad(LoadAdError loadAdError) {
// Called when an app open ad has failed to load.
Log.d(LOG_TAG, loadAdError.getMessage());
isLoadingAd = false;
}
});
}
...
}
Kotlin
private inner class AppOpenAdManager {
...
/** Request an ad. */
fun loadAd(context: Context) {
// Do not load ad if there is an unused ad or one is already loading.
if (isLoadingAd || isAdAvailable()) {
return
}
isLoadingAd = true
val request = AdRequest.Builder().build()
AppOpenAd.load(
context, AD_UNIT_ID, request,
AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
object : AppOpenAdLoadCallback() {
override fun onAdLoaded(ad: AppOpenAd) {
// Called when an app open ad has loaded.
Log.d(LOG_TAG, "Ad was loaded.")
appOpenAd = ad
isLoadingAd = false
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
// Called when an app open ad has failed to load.
Log.d(LOG_TAG, loadAdError.message)
isLoadingAd = false;
}
})
}
...
}
AppOpenAdLoadCallback
에는 AppOpenAd
로드가 완료되면 호출되는 메서드가 있습니다.
현재 활동 추적
광고를 게재하려면 Activity
컨텍스트가 필요합니다. 사용자가 사용 중인 가장 최근 활동을 추적하려면 Application
클래스에서 Application.ActivityLifecycleCallbacks
를 구현합니다.
Java
public class MyApplication extends Application implements ActivityLifecycleCallbacks {
private Activity currentActivity;
...
/** ActivityLifecycleCallback methods. */
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
@Override
public void onActivityStarted(Activity activity) {
// Updating the currentActivity only when an ad is not showing.
if (!appOpenAdManager.isShowingAd) {
currentActivity = activity;
}
}
@Override
public void onActivityResumed(Activity 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) {}
}
Kotlin
class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
private var currentActivity: Activity? = null
...
/** ActivityLifecycleCallback methods. */
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {
// Updating the currentActivity only when an ad is not showing.
if (!appOpenAdManager.isShowingAd) {
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) {}
}
현재 활동을 추적하면 광고를 표시하는 데 사용할 수 있는 컨텍스트를
확보할 수 있습니다. 이제 registerActivityLifecycleCallbacks
메서드를 사용하여 이 인터페이스를 등록해야 합니다.
Java
public class MyApplication extends Application {
...
@Override
public void onCreate() {
super.onCreate();
this.registerActivityLifecycleCallbacks(this);
new Thread(
() -> {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this, initializationStatus -> {});
})
.start();
appOpenAdManager = new AppOpenAdManager();
}
}
Kotlin
class MyApplication : Application() {
...
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(this)
val backgroundScope = CoroutineScope(Dispatchers.IO)
backgroundScope.launch {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this@MyApplication) {}
}
appOpenAdManager = AppOpenAdManager()
}
}
registerActivityLifecycleCallbacks
를 사용하면 모든 Activity
이벤트를 수신 대기할 수 있습니다. 활동이 시작되고 소멸되는 시점을 수신 대기하면
현재 Activity
참조를 추적할 수 있으며,
이 참조는 앱 오프닝 광고를 표시하는 데 사용됩니다.
광고 표시 및 전체 화면 콜백 이벤트 처리
다음 코드는 광고를 게재한 후 새로고침하는 시기를 보여줍니다.
Java
public class MyApplication extends Application {
...
/** Interface definition for a callback to be invoked when an app open ad is complete. */
public interface OnShowAdCompleteListener {
void onShowAdComplete();
}
private class AppOpenAdManager {
...
/** Shows the ad if one isn't already showing. */
public void showAdIfAvailable(
@NonNull final Activity activity,
@NonNull OnShowAdCompleteListener onShowAdCompleteListener){
// If the app open ad is already showing, do not show the ad again.
if (isShowingAd) {
Log.d(LOG_TAG, "The app open ad is already showing.");
return;
}
// If the app open ad is not available yet, invoke the callback then load the ad.
if (!isAdAvailable()) {
Log.d(LOG_TAG, "The app open ad is not ready yet.");
onShowAdCompleteListener.onShowAdComplete();
loadAd(activity);
return;
}
appOpenAd.setFullScreenContentCallback(
new FullScreenContentCallback() {
@Override
public void onAdDismissedFullScreenContent() {
// Called when fullscreen content is dismissed.
// Set the reference to null so isAdAvailable() returns false.
Log.d(LOG_TAG, "Ad dismissed fullscreen content.");
appOpenAd = null;
isShowingAd = false;
onShowAdCompleteListener.onShowAdComplete();
loadAd(activity);
}
@Override
public void onAdFailedToShowFullScreenContent(AdError adError) {
// Called when fullscreen content failed to show.
// Set the reference to null so isAdAvailable() returns false.
Log.d(LOG_TAG, adError.getMessage());
appOpenAd = null;
isShowingAd = false;
onShowAdCompleteListener.onShowAdComplete();
loadAd(activity);
}
@Override
public void onAdShowedFullScreenContent() {
// Called when fullscreen content is shown.
Log.d(LOG_TAG, "Ad showed fullscreen content.");
}
});
isShowingAd = true;
appOpenAd.show(activity);
}
...
}
}
Kotlin
class MyApplication : Application() {
...
/** Interface definition for a callback to be invoked when an app open ad is complete. */
interface OnShowAdCompleteListener {
fun onShowAdComplete()
}
private inner class AppOpenAdManager {
...
/** Shows the ad if one isn't already showing. */
fun showAdIfAvailable(
activity: Activity,
onShowAdCompleteListener: OnShowAdCompleteListener) {
// If the app open ad is already showing, do not show the ad again.
if (isShowingAd) {
Log.d(LOG_TAG, "The app open ad is already showing.")
return
}
// If the app open ad is not available yet, invoke the callback then load the ad.
if (!isAdAvailable()) {
Log.d(LOG_TAG, "The app open ad is not ready yet.")
onShowAdCompleteListener.onShowAdComplete()
loadAd(activity)
return
}
appOpenAd?.setFullScreenContentCallback(
object : FullScreenContentCallback() {
override fun onAdDismissedFullScreenContent() {
// Called when full screen content is dismissed.
// Set the reference to null so isAdAvailable() returns false.
Log.d(LOG_TAG, "Ad dismissed fullscreen content.")
appOpenAd = null
isShowingAd = false
onShowAdCompleteListener.onShowAdComplete()
loadAd(activity)
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
// Called when fullscreen content failed to show.
// Set the reference to null so isAdAvailable() returns false.
Log.d(LOG_TAG, adError.message)
appOpenAd = null
isShowingAd = false
onShowAdCompleteListener.onShowAdComplete()
loadAd(activity)
}
override fun onAdShowedFullScreenContent() {
// Called when fullscreen content is shown.
Log.d(LOG_TAG, "Ad showed fullscreen content.")
}
})
isShowingAd = true
appOpenAd?.show(activity)
}
...
}
}
FullScreenContentCallback
는 광고가 표시되거나
표시되지 않을 때, 광고가 닫힐 때와 같은 이벤트를 처리합니다. 사용자가 앱에서 나간 후 앱 오프닝 광고를 클릭하여
돌아오면 이 사용자에게 또 다른 앱 오프닝 광고가
표시되지 않습니다.
앱 포그라운드 이벤트 수신 대기
gradle 파일에 라이브러리 추가
앱 포그라운드 이벤트에 관한 알림을 받으려면 LifecycleObserver
를 등록해야 합니다. 먼저 LifecycleObserver
라이브러리를 포함하도록 애플리케이션 수준의 build.gradle
파일을 수정합니다.
apply plugin: 'com.android.application'
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.gms:play-services-ads:23.2.0'
def lifecycle_version = "2.3.1"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
}
LifecycleObserver 인터페이스 구현
LifecycleObserver
인터페이스를 구현하여 Application
클래스에서 포그라운드 이벤트를 수신 대기할 수 있습니다.
Java
public class MyApplication extends Application
implements ActivityLifecycleCallbacks, LifecycleObserver { {
...
@Override
public void onCreate() {
super.onCreate();
this.registerActivityLifecycleCallbacks(this);
new Thread(
() -> {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this, initializationStatus -> {});
})
.start();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
appOpenAdManager = new AppOpenAdManager();
}
/** LifecycleObserver method that shows the app open ad when the app moves to foreground. */
@OnLifecycleEvent(Event.ON_START)
protected void onMoveToForeground() {
// Show the ad (if available) when the app moves to foreground.
appOpenAdManager.showAdIfAvailable(currentActivity);
}
/** Show the ad if one isn't already showing. */
private void showAdIfAvailable(@NonNull final Activity activity) {
showAdIfAvailable(
activity,
new OnShowAdCompleteListener() {
@Override
public void onShowAdComplete() {
// Empty because the user will go back to the activity that shows the ad.
}
});
}
}
Kotlin
class MyApplication : Application(),
Application.ActivityLifecycleCallbacks, LifecycleObserver {
...
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(this)
val backgroundScope = CoroutineScope(Dispatchers.IO)
backgroundScope.launch {
// Initialize the Google Mobile Ads SDK on a background thread.
MobileAds.initialize(this@MyApplication) {}
}
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
appOpenAdManager = AppOpenAdManager()
}
/** LifecycleObserver method that shows the app open ad when the app moves to foreground. */
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
// Show the ad (if available) when the app moves to foreground.
currentActivity?.let {
appOpenAdManager.showAdIfAvailable(it)
}
}
/** Show the ad if one isn't already showing. */
fun showAdIfAvailable(activity: Activity) {
showAdIfAvailable(
activity,
object : OnShowAdCompleteListener {
override fun onShowAdComplete() {
// Empty because the user will go back to the activity that shows the ad.
}
})
}
}
LifecycleObserver
를 등록하면 앱에 앱 실행 및 포그라운드 이벤트가 전달되며
적절한 시점에 광고를 표시할 수 있게 됩니다.
광고 만료 고려
만료된 광고가 표시되지 않도록 하려면 AppOpenAdManager
에 광고 참조가 로드된 후 경과한 시간을 확인하는 메서드를 추가합니다. 그런 다음 이 메서드를
사용하여 광고가 여전히 유효한지 확인합니다.
Java
private 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;
/** Request an ad. */
public void loadAd(Context context) {
// Do not load ad if there is an unused ad or one is already loading.
if (isLoadingAd || isAdAvailable()) {
return;
}
isLoadingAd = true;
AdRequest request = new AdRequest.Builder().build();
AppOpenAd.load(
context, AD_UNIT_ID, request,
AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
new AppOpenAdLoadCallback() {
@Override
public void onAdLoaded(AppOpenAd ad) {
// Called when an app open ad has loaded.
Log.d(LOG_TAG, "Ad was loaded.");
appOpenAd = ad;
isLoadingAd = false;
loadTime = (new Date()).getTime();
}
@Override
public void onAdFailedToLoad(LoadAdError loadAdError) {
// Called when an app open ad has failed to load.
Log.d(LOG_TAG, loadAdError.getMessage());
isLoadingAd = false;
}
});
}
...
/** 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));
}
/** Check if ad exists and can be shown. */
public boolean isAdAvailable() {
return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
}
}
Kotlin
private inner class 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
/** Request an ad. */
fun loadAd(context: Context) {
// Do not load ad if there is an unused ad or one is already loading.
if (isLoadingAd || isAdAvailable()) {
return
}
isLoadingAd = true
val request = AdRequest.Builder().build()
AppOpenAd.load(
context, AD_UNIT_ID, request,
AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
object : AppOpenAdLoadCallback() {
override fun onAdLoaded(ad: AppOpenAd) {
// Called when an app open ad has loaded.
Log.d(LOG_TAG, "Ad was loaded.")
appOpenAd = ad
isLoadingAd = false
loadTime = Date().time
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
// Called when an app open ad has failed to load.
Log.d(LOG_TAG, loadAdError.message)
isLoadingAd = false;
}
})
}
...
private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean {
val dateDifference: Long = Date().time - loadTime
val numMilliSecondsPerHour: Long = 3600000
return dateDifference < numMilliSecondsPerHour * numHours
}
private fun isAdAvailable(): Boolean {
return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4)
}
}
콜드 스타트 및 로드 화면
지금까지 이 문서에서는 메모리에 정지된 앱을 사용자가 포그라운드로 가져올 때만 앱 오프닝 광고를 표시한다고 가정합니다. '콜드 스타트'는 앱이 실행되었지만 이전에 메모리에서 정지되지 않은 경우 발생합니다.
콜드 스타트의 예로는 사용자가 앱을 처음 여는 경우를 들 수 있습니다. 콜드 스타트를 사용하면 이전에 로드되어 바로 표시할 수 있는 앱 오프닝 광고가 없습니다. 광고를 요청한 후 광고를 다시 받는 시점 사이의 지연으로 인해 사용자가 잠시 앱을 사용할 수 있게 된 후 갑작스러운 광고가 표시될 수 있습니다. 이는 사용자 환경에 악영향을 미치므로 피해야 합니다.
콜드 스타트 시 앱 오프닝 광고를 사용하는 좋은 방법은 로드 화면을 사용하여 게임 또는 앱 애셋을 로드하고 로드 화면에만 광고를 표시하는 것입니다. 앱에서 로드가 완료되고 사용자를 앱의 주요 콘텐츠로 안내한 경우에는 광고를 표시하지 않습니다.
권장사항
앱 오프닝 광고를 사용하면 앱이 처음 실행될 때와 앱이 전환되는 중에 앱의 로드 화면에서 수익을 창출할 수 있지만, 사용자가 앱을 즐겁게 사용할 수 있도록 권장사항을 염두에 두는 것이 중요합니다. 가장 좋은 방법은 다음과 같습니다.
- 사용자가 앱을 몇 번 사용한 후에 첫 앱 오프닝 광고를 게재하세요.
- 사용자가 앱이 로드되기를 기다리는 동안 앱 오프닝 광고를 게재하세요.
- 앱 오프닝 광고 아래에 로드 화면이 있고 광고를 닫기 전에
화면 로드가 완료되면
onAdDismissedFullScreenContent()
메서드로 로드 화면을 닫는 것이 좋습니다.
GitHub의 예
다음 단계
다음 주제를 살펴보세요.