이 가이드는 Google 모바일 광고 Android SDK를 사용하여 앱 오프닝 광고를 통합하는 게시자를 대상으로 작성되었습니다.
앱 오프닝 광고는 앱 로드 화면에서 수익을 올리려는 게시자를 위해 개발된 특별한 광고 형식입니다. 앱 오프닝 광고는 언제든지 닫을 수 있으며 사용자가 앱을 포그라운드로 가져올 때 표시되도록 설계되었습니다.
앱 오프닝 광고는 사용자가 앱을 사용 중임을 알 수 있도록 작은 브랜딩 영역을 자동으로 표시합니다. 앱 오프닝 광고는 다음과 같이 게재됩니다.
개략적인 단계는 다음과 같습니다.
Application
클래스를 확장하여 Google 모바일 광고 SDK를 초기화합니다.- 광고를 게재하기 전에 광고를 로드하는 유틸리티 클래스를 만듭니다.
- 광고를 로드합니다.
ActivityLifecycleCallbacks.
듣기- 광고를 표시하고 콜백을 처리합니다.
- 포그라운드 이벤트 중에 광고를 표시하도록
LifecycleObserver
인터페이스를 구현하고 등록합니다.
기본 요건
- Google 모바일 광고 SDK 19.4.0 이상
- 시작 가이드의 설정 안내를 따릅니다.
항상 테스트 광고로 테스트
앱을 제작하고 테스트할 때는 운영 중인 실제 광고가 아닌 테스트 광고를 사용해야 합니다. 이렇게 하지 않으면 계정이 정지될 수 있습니다.
테스트 광고를 로드하는 가장 쉬운 방법은 앱 오프닝 광고 전용 테스트 광고 단위 ID를 사용하는 것입니다.
ca-app-pub-3940256099942544/3419835294
이 ID는 모든 요청에 대해 테스트 광고를 반환하도록 특별히 구성되었으며, 코딩, 테스트 및 디버깅 중에 앱에서 자유롭게 사용할 수 있습니다. 앱을 게시하기 전에 이 ID를 자체 광고 단위 ID로 바꿔야 합니다.
모바일 광고 SDK의 테스트 광고가 작동하는 방식을 자세히 알아보려면 테스트 광고를 참고하세요.
Application 클래스 확장
Application
클래스를 확장하는 새 클래스를 만들고 다음 코드를 추가하여
Google 모바일 광고 SDK를 초기화합니다.
자바
/** Application class that initializes, loads and show ads when activities change states. */ public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); MobileAds.initialize( this, new OnInitializationCompleteListener() { @Override public void onInitializationComplete(InitializationStatus initializationStatus) {} }); } }
Kotlin
/** Application class that initializes, loads and show ads when activities change states. */ class MyApplication : Application() { override fun onCreate() { super.onCreate() MobileAds.initialize(this) {} } }
그러면 SDK가 초기화되고 나중에 앱 포그라운드 이벤트에 등록할 수 있는 스켈레톤이 제공됩니다.
다음으로 AndroidManifest.xml
에 다음 코드를 추가합니다.
<application
android:name="com.google.android.gms.example.appopendemo.MyApplication" ...>
...
</application>
실제 패키지 이름을 언급해야 합니다.
유틸리티 클래스 구현
광고는 빠르게 게재되어야 하므로 광고를 게재하기 전에 로드하는 것이 좋습니다. 이렇게 하면 사용자가 앱에 들어가자마자 광고가 게재될 수 있습니다. 광고를 표시해야 하는 시점에 앞서 광고 요청을 할 수 있도록 유틸리티 클래스를 구현합니다.
MyApplication
클래스 내에 AppOpenAdManager
라는 새 클래스를 만들고 다음과 같이 작성합니다.
자바
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/3419835294"; 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/3419835294" 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
클래스에서 인스턴스화할 수 있습니다.
자바
public class MyApplication extends Application { private AppOpenAdManager appOpenAdManager; @Override public void onCreate() { super.onCreate(); MobileAds.initialize( this, new OnInitializationCompleteListener() { @Override public void onInitializationComplete(InitializationStatus initializationStatus) {} }); appOpenAdManager = new AppOpenAdManager(this); } }
Kotlin
class MyApplication : Application() { private lateinit var appOpenAdManager: AppOpenAdManager override fun onCreate() { super.onCreate() MobileAds.initialize(this) {} appOpenAdManager = AppOpenAdManager() } }
광고 로드
다음 단계는 loadAd()
메서드를 작성하는 것입니다.
자바
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
를 구현합니다.
자바
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
메서드를 사용하여 이 인터페이스를 등록해야 합니다.
자바
public class MyApplication extends Application { ... @Override public void onCreate() { super.onCreate(); this.registerActivityLifecycleCallbacks(this); MobileAds.initialize( this, new OnInitializationCompleteListener() { @Override public void onInitializationComplete(InitializationStatus initializationStatus) {} }); appOpenAdManager = new AppOpenAdManager(); } }
Kotlin
class MyApplication : Application() { ... override fun onCreate() { super.onCreate() registerActivityLifecycleCallbacks(this) MobileAds.initialize(this) {} appOpenAdManager = AppOpenAdManager() } }
registerActivityLifecycleCallbacks
를 사용하면 모든 Activity
이벤트를 수신 대기할 수 있습니다. 활동이 시작되고 소멸되는 시점을 수신 대기하면 앱 오프닝 광고를 표시하는 데 사용할 현재 Activity
에 대한 참조를 추적할 수 있습니다.
광고 표시 및 전체 화면 콜백 이벤트 처리
다음 코드는 광고를 표시한 후 다시 로드하는 시점을 보여줍니다.
자바
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:21.5.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
클래스에서 포그라운드 이벤트를 수신 대기할 수 있습니다.
자바
public class MyApplication extends Application implements ActivityLifecycleCallbacks, LifecycleObserver { { ... @Override public void onCreate() { super.onCreate(); this.registerActivityLifecycleCallbacks(this); MobileAds.initialize( this, new OnInitializationCompleteListener() { @Override public void onInitializationComplete(InitializationStatus initializationStatus) {} }); 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) MobileAds.initialize(this) {} 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
에 추가하세요. 그런 다음 이 메서드를 이용해 광고가 여전히 유효한지 확인하세요.
자바
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()
메서드로 로드 화면을 닫는 것이 좋습니다.