/ ADMOB, MONETIZATION, ANDROID

앱 시작과 함께 광고를 보여주려면? - 애드몹 앱 오프닝 광고

이 글에서 소개하는 앱 오프닝 광고에 대한 자세한 내용은 AdMob On Air 에서 영상으로도 시청하실 수 있습니다.

특히, 아래에 소개되는 예제 코드를 사용하여 앱 오프닝 광고를 앱에 통합하는 과정을 자세히 다루고 있으니, 자세한 과정이 궁금하신 분들은 영상을 꼭 시청해주세요!

영상 시청하기


사용자가 앱을 사용하려면 반드시 거치는 단계는 무엇일까요? 어떻게 보면 당연한(?) 이야기겠지만, 바로 앱 실행 단계입니다. 사용자 특성에 따라 도달률이 달라지는 다른 단계와는 달리, 앱 실행 단계는 앱을 실행할 때마다 반드시 거치게 됩니다.

앱 수익화 관점에서 이 단계를 평가한다면, 이는 사용자 도달률과 노출률이 모두 높은 지면이기에 매우 높은 가치를 부여할 수 있을 것입니다. 그렇다면, 어떻게 해야 이 지면을 통해 수익을 얻을 수 있을까요? 여러 방법이 있겠지만, 이번에 애드몹에서 오픈 베타로 출시된 앱 오프닝 광고를 사용하면 광고를 통해 추가 수익을 창출할 수 있습니다.

앱 오프닝 광고란?

앱 오프닝 광고 (App Open Ads)는 앱이 실행되는 시점에 표시할 수 있는 광고 포맷으로, 앱이 실행된 후 표시되는 로고 화면 (스플래시) 혹은 로딩 화면 등에 노출할 수 있습니다. 다음은 앱 오프닝 광고의 권장되는 구현 예를 보여줍니다.

AdMob App Open Ads

가이드라인

권장되는 구현 방식

앞서 본 영상을 단계별로 요약하면 다음과 같습니다.

Recommended implementation

  1. 홈 스크린에서 앱 실행
  2. 스플래시/로딩 화면 표시
  3. 스플래시/로딩 화면 위에 앱 오프닝 광고 표시
  4. 광고를 닫은 후, 스플래시 화면이 종료되면서 메인 화면으로 이동

앱 오프닝 광고를 구현할 때 가장 유의해야 하는 단계는 3번 단계로, 광고 뒷 배경에 스플래시/로딩 화면이 유지되는지 꼭 확인해야 합니다.

앱 실행 시점과 더불어, 사용자가 기존에 사용하던 앱으로 복귀하는 시점에도 앱 오프닝 광고를 보여줄 수 있습니다. 다음은 사용중이던 앱으로 복귀하는 시점에 앱 오프닝 광고를 보여주는 사례를 보여줍니다.

Showing App Ads when a user switching back to the app

권장하지 않는 구현 방식

가이드라인에서 권장하지 않는 구현 사례도 몇 가지 살펴보도록 하겠습니다.

  • 앱 오프닝 광고 배경에 아무런 컨텐츠가 표시되지 않는 경우: 광고가 표시되기 직전에 보였던 화면이 그대로 유지되지 않았습니다.

Discouraged implementation

  • 앱 오프닝 광고가 앱 컨텐츠 화면이 노출된 이후에 표시되는 경우: 앱 오프닝 광고는 사용자가 앱과 상호작용을 할 수 있는 시점 이전에만 노출해야 합니다.

Discouraged implementation

구현 전에 참고할 사항

광고 노출 관련

여타 포맷과 마찬가지로, 앱 오프닝 광고도 광고를 보여주려면 사전에 광고를 불러온 상태여야 합니다. 따라서, 다음과 같은 경우에는 광고를 노출할 수 없습니다.

  • 앱 설치 후 최초로 실행하는 경우
  • 광고를 미리 불러온 상태에서 백그라운드 상태로 전환된 앱이 완전히 종료 (메모리에서 제거)된 후 다시 시작되는 경우
    • 앱이 완전히 종료될 때, 광고 또한 메모리에서 함께 제거됩니다
  • 광고가 더이상 유효하지 않은 경우
    • 광고 요청 후 4시간1이 지난 광고는 무효화 됩니다. 따라서 이 경우 캐싱된 광고를 보여주는 대신 새 광고를 요청해야 합니다.

마지막 항목 (광고 유효시간)에서 확인할 수 있듯이, 사용자가 마지막으로 앱을 실행한 후 4시간 이내에 앱을 다시 실행해야만 앱 오프닝 광고를 정상적으로 표시할 수 있습니다. 따라서 사용자가 짧은 주기로 앱을 실행하는 앱 (예: 방치형 게임 등)에서 사용해야 최대의 효과를 발휘할 수 있습니다.

앱 브랜딩 영역

앱이 구글 플레이/앱스토어에 등록되어 있다면 앱 설치 광고 상단에 앱 이름 및 아이콘이 다음과 같이 자동으로 표시됩니다.

단, 앱이 아직 스토어에 등록되지 않았거나 패키지 이름이 스토어에 등록된 이름과 다른 경우 다음과 같이 앱 이름이나 아이콘이 표시되지 않습니다. 앱을 스토어에 등록하지 않는 한, 이를 수동으로 설정할 수 있는 방법은 아직 없습니다.

앱 오프닝 광고 구현하기 (안드로이드)

안드로이드에서 앱 오프닝 광고를 구현하는 방법을 살펴보겠습니다. 만들어볼 예제 앱은 액티비티 두개로 구성되어 있으며, 각 액티비티에서 앱 오프닝 광고를 노출하게 구성되어 있습니다.

Google Mobile Ads SDK 추가

앱 오프닝 광고를 사용하려면 Google Mobile Ads (GMA) SDK 19.4.0 이상 버전을 사용해야 합니다. app/build.gradle를 연 후, dependencies 섹션을 다음과 같이 변경합니다.

[app/build.gradle]

apply plugin: 'com.android.application'

android {
    ...
}

dependencies {
    ...
    // Google Mobile Ads SDK를 추가합니다.
    implementation "com.google.android.gms:play-services-ads:19.4.0"
}

애드몹 애플리케이션 ID 설정

GMA SDK를 사용하려면 앱 매니페스트(AndroidManifest.xml)에 자신의 앱의 애드몹 앱 ID (ca-app-pub-xxxx/yyyy 형태)를 추가해야 합니다. 아래는 애드몹 테스트 앱 ID를 매니페스트에 추가한 예를 보여줍니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application>
        ...

        <!-- 애드몹 앱 ID 추가-->
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-3940256099942544~3347511713" />
    </application>

</manifest>

AppOpenAdManager 클래스 작성

앱 오프닝 광고를 쉽게 제어할 수 있도록 AppOpenAdManager 유틸리티 클래스를 작성합니다. 유틸리티 클래스에서 작성할 메서드는 다음과 같습니다.

  • showAdIfAvailable(): 사용할 수 있는 앱 오프닝 광고가 있는 경우 광고를 표시합니다.
  • fetchAd(): 앱 오프닝 광고를 불러옵니다.
  • isAdAvailable(): 사용 가능한 (캐시되어 있고 유효한) 앱 오프닝 광고가 있는지 확인합니다.
  • isAdExpired(): 캐시되어 있는 앱 오프닝 광고가 만료되었는지 확인합니다.

메서드를 구현하기에 앞서, 클래스 내에서 사용할 필드를 정의한 후 생성자를 구현합니다.

public class AppOpenAdManager {

    private static final String TAG = "AppOpenManager";

    public static final String AD_UNIT_ID = "ca-app-pub-3940256099942544/1033173712";

    public static final long AD_EXPIRY_DURATION = 3600000 * 4;

    private final Application application;

    private Activity mostCurrentActivity;

    private AppOpenAd ad;

    private boolean isShowingAd = false;

    private long lastAdFetchTime = 0L;

    public AppOpenAdManager(@NonNull Application application) {
        this.application = application;
    }

    ...
}

각 필드의 용도는 다음과 같습니다.

  • AD_UNIT_ID: 앱 오프닝 광고의 광고 단위 ID 입니다. (위에 표시된 ID는 테스트 광고 단위입니다)
  • AD_EXPIRY_DURATION: 광고의 유효 시간을 밀리초 (ms)로 표현한 수치로, 4시간으로 설정되어 있습니다.
  • application: 애플리케이션 인스턴스를 참조하기 위해 사용합니다. (자세한 내용은 이후 단계에서 별도 설명)
  • mostCurrentActivity: 가장 최근에 실행된 액티비티를 참조하기 위해 사용합니다. (자세한 내용은 이후 단계에서 별도 설명)
  • ad: 앱 오프닝 광고 인스턴스로, 미리 불러온 광고를 저장하는 데 사용합니다.
  • isShowingAd: 앱 오프닝 광고가 현재 노출되고 있는 중인지 상태를 저장합니다.
  • lastAdFetchTime: 앱 오프닝 광고를 마지막으로 받아온 시간을 저장하며, 광고 만료 여부를 확인할 때 사용합니다.

다음으로, 메서드 AppOpenAdManager 클래스에서 구현할 메서드를 정의합니다. 각 메서드의 몸체는 이후 단계에서 구현하므로 이 단계에서는 큰 틀을 만들어 주기만 합니다.

private boolean isAdExpired() {
    // TODO: Implement method
    return false;
}

private boolean isAdAvailable() {
    // TODO: Implement method
    return false;
}

private void fetchAd() {
    // TODO: Implement method
}

public void showAdIfAvailable(@Nullable final FullScreenContentCallback listener) {
    // TODO: Implement method
}

먼저 isAdExpired()isAdAvailable() 메서드를 다음과 같이 구현합니다. isAdExpired() 메서드는 불러온 광고의 만료 여부를 확인하며, isAdAvailable()은 현재 불러온 앱 설치가 있는지, 또한 이 광고가 아직 유효한지 확인합니다.

private boolean isAdExpired() {
    return System.currentTimeMillis() - lastAdFetchTime > AD_EXPIRY_DURATION;
}

private boolean isAdAvailable() {
    return this.ad != null && !isAdExpired();
}

다음, 앱 오프닝 광고 요청에 대한 응답을 처리할 수 있도록 AppOpenAdManager 클래스에 콜백을 구현합니다.

다음과 같이 AppOpenAdManagerAppOpenAd.AppOpenAdLoadCallback을 상속하게 변경한 후, onAppOpenAdLoaded()onAppOpenAdFailedToLoad() 콜백 메서드를 구현합니다.

public class AppOpenAdManager2 extends AppOpenAd.AppOpenAdLoadCallback {
    ...

    // AppOpenAd.AppOpenAdLoadCallback implementations

    @Override
    public void onAppOpenAdLoaded(AppOpenAd ad) {
        Log.d(TAG, "Ad loaded");
        this.lastAdFetchTime = System.currentTimeMillis();
        this.ad = ad;
    }

    @Override
    public void onAppOpenAdFailedToLoad(LoadAdError error) {
        Log.d(TAG, "Failed to load an ad: " + error.getMessage());
    }
}

콜백 구현이 완료되었다면 fetchAd() 메서드를 구현합니다. isAvailable() 메서드를 사용하여 이미 사용할 수 있는 광고가 있다면 추가로 로드를 하지 않고, 그렇지 않은 경우에는 AppOpenAd.load() 메서드를 사용하여 앱 오프닝 광고를 로드합니다. 앱 오프닝 광고는 세로 화면 (AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT)과 가로 화면 (AppOpenAd.APP_OPEN_AD_ORIENTATION_LANDSCAPE)을 모두 지원하므로, 원하는 레이아웃에 맞게 광고를 요청할 수 있습니다.

private void fetchAd() {
    if (isAdAvailable()) {
        return;
    }

    AdRequest request = new AdRequest.Builder().build();
    AppOpenAd.load(application, AD_UNIT_ID, 
        request, AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, this);
}

다음으로 앱 오프닝 광고를 보여주는 메서드인 showAdIfAvailable()를 구현합니다. 현재 광고가 표시되고 있는 상태이거나 보여줄 수 있는 광고가 없는 경우에는 광고를 표시하지 않습니다. 또한, FulScreenContentCallback 파라미터를 제공하는 경우 앱 오프닝 광고의 표시 상태에 대한 콜백을 받을 수 있게끔 구성되어 있습니다.

앱 오프닝 광고는 AppOpenAd.show() 메서드를 사용하면 앱 오프닝 광고를 표시할 수 있습니다. 앱 오프닝 광고를 표시하려면 액티비티가 필요한데, 이 예제에서는 앱 내에서 가장 최근에 표시된 (즉, 활성 상태인) 액티비티 정보를 사용하여 광고를 표시하는 것을 확인할 수 있습니다.

추가로, FullScreenContentCallback를 인자로 받지 않는 경우를 위해 아무 파라미터를 받지 않는 버전의 메서드도 함께 추가합니다.

public void showAdIfAvailable(@Nullable final FullScreenContentCallback listener) {
    if (this.isShowingAd) {
        Log.e(TAG, "Can't show the ad: Already showing the ad");
        return;
    }

    if (!isAdAvailable()) {
        Log.d(TAG, "Can't show the ad: Ad not available");
        fetchAd();
        return;
    }

    FullScreenContentCallback callback = new FullScreenContentCallback() {
        @Override
        public void onAdFailedToShowFullScreenContent(AdError error) {
            if (listener != null) {
                listener.onAdFailedToShowFullScreenContent(error);
            }
        }

        @Override
        public void onAdShowedFullScreenContent() {
            if (listener != null) {
                listener.onAdShowedFullScreenContent();
            }
            AppOpenAdManager.this.isShowingAd = true;
        }

        @Override
        public void onAdDismissedFullScreenContent() {
            if (listener != null) {
                listener.onAdDismissedFullScreenContent();
            }
            isShowingAd = false;
            AppOpenAdManager.this.ad = null;
            fetchAd();
        }
    };

    ad.show(mostCurrentActivity, callback);
}

public void showAdIfAvailable() {
    showAdIfAvailable(null);
}

앱 설치 광고를 표시하기 위해 필요한 액티비티 정보는 얻으려면 ActivityLifecycleCallbacks 인터페이스를 구현해야 합니다. 다음과 같이 AppOpenAdManager 클래스에 Application.ActivityLifecycleCallbacks를 구현합니다.

가장 최근에 활성화 되어있는 액티비티의 정보를 추적하기 위해, onActivityResumed() 콜백 메서드를 통해 얻은 액티비티의 인스턴스를 mostCurrentActivity 인스턴스에 저장합니다.

public class AppOpenAdManager extends AppOpenAd.AppOpenAdLoadCallback
        implements Application.ActivityLifecycleCallbacks {

   // Application.ActivityLifecycleCallbacks implementations

    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        // Do nothing
    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {
        // Do nothing
    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {
        this.mostCurrentActivity = activity;
    }

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

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

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
        // Do nothing
    }

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

커스텀 Application 클래스 작성

AppOpenAdManager는 애플리케이션 레벨에서 작동하므로, 커스텀 애플리케이션 클래스를 작성한 후 AppOpenAdManager의 인스턴스를 생성해야 합니다.

먼저, 다음과 같이 Application 클래스를 상송하는 커스텀 애플리케이션 클래스를 생성합니다. 아래 예시에서는 MyApplication이라는 이름으로 클래스를 생성한 예를 보여줍니다.

public class MyApplication extends Application {

}

다음으로, AppOpenAdManager를 멤버로 추가한 후 onCreate()에서 인스턴스를 생성합니다. 이 때, MobileAds.initialize() 메서드를 사용하여 SDK 초기화도 함께 수행합니다.

그리고 getAppOpenAdManager() 메서드를 추가하여 앱 내 다른 컴포넌트 (예: 액티비티)에서 AppOpenAdManager 인스턴스를 참조할 수 있도록 합니다.

private AppOpenAdManager appOpenAdManager;

    @Override
    public void onCreate() {
        super.onCreate();

        MobileAds.initialize(this);

        appOpenAdManager = new AppOpenAdManager(this,
                AppOpenAdManager.TEST_AD_UNIT_ID,
                AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT);
    }

    public AppOpenAdManager getAppOpenAdManager() {
        return this.appOpenAdManager;
    }

기본 애플리케이션 클래스 대신 커스텀 애플리케이션 클래스를 사용하려면 앱 매니페스트를 수정해야 합니다. AndroidManifest.xml 파일을 연 후, application 태그의 android:name항목에 앞에서 생성한 커스텀 애플리케이션 클래스를 지정합니다. (아래 예는 MyApplication 클래스가 com.androidhuman.ads.appopenads 패키지에 있는 경우의 예를 보여줍니다)

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

    <application
        ...
        android:name=".MyApplication">

        ...
    </application>

</manifest>

앱 내에 앱 오프닝 광고 추가하기

액티비티 매니페스트 설정 업데이트

구현해볼 예제 애플리케이션은 스플래시 (SplashActivity)와 메인 (MainActivity) 두 개의 액티비티로 구성됩니다. 액티비티 클래스를 추가한 후, 스플래시 액티비티가 먼저 실행되도록 다음과 같이 매니페스트를 수정합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest>

    <application>

        <!-- SplashActivity를 Launcher 액티비티로 설정합니다 -->
        <activity
            android:name=".SplashActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".MainActivity"/>

        ...
    </application>

</manifest>

스플래시 액티비티 작성

스플래시 화면에 표시되는 동안 앱 오프닝 광고가 표시될 수 있도록 액티비티 코드를 다음과 같이 작성합니다.

public class SplashActivity extends AppCompatActivity {

    private static final String TAG = "SplashActivity";

    AppOpenAdManager appOpenAdManager;

    private boolean isAdShown = false;

    private boolean isAdDismissed = false;

    private boolean isLoadCompleted = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        appOpenAdManager = ((MyApplication) getApplication()).getAppOpenAdManager();

        loadResources();
        appOpenAdManager.showAdIfAvailable(new FullScreenContentCallback() {

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

            @Override
            public void onAdDismissedFullScreenContent() {
                isAdDismissed = true;

                if (isLoadCompleted) {
                    launchMainScreen();
                } else {
                    Log.d(TAG, "Waiting resources to be loaded...");
                }
            }
        });
    }

    private void loadResources() {
        // Wait for 5 seconds
        CountDownTimer timer = new CountDownTimer(5000L, 1000L) {
            @Override
            public void onTick(long millisUntilFinished) {
                // Do nothing
            }

            @Override
            public void onFinish() {
                isLoadCompleted = true;

                // Check whether App Open ad was shown or not.
                if (isAdShown) {
                    // Check App Open ad was dismissed or not.
                    if (isAdDismissed) {
                        launchMainScreen();
                    } else {
                        Log.d(TAG, "Waiting for ad to be dismissed...");
                    }
                } else {
                    launchMainScreen();
                }
            }
        };
        timer.start();
    }

    private void launchMainScreen() {
        ActivityCompat.finishAffinity(SplashActivity.this);
        startActivity(new Intent(SplashActivity.this, MainActivity.class));
    }
}

광고가 표시되고 있는 도중에 로드가 완료되는 경우, 바로 다른 화면으로 전환하지 않고 광고가 닫힐 때까지 상태를 유지해야 합니다. loadResources() 메서드와 onCreate() 메서드의 showAdIfAvailable() 메서드 호출부를 보면 광고 표시 여부 및 로드 완료 여부에 따라 화면 전환 여부가 달라지는 것을 확인할 수 있습니다.

메인 액티비티 작성

메인 액티비티에서는 사용자가 메인 액티비티로 복귀했을 때 앱 오프닝 광고를 표시합니다. 메인 화면에 복귀할 때마다 매번 광고를 보여준다면 사용자 경험에 해가 되므로, 예제에서는 세번에 한번 꼴로 광고를 보여주도록 설정했습니다.

public class MainActivity extends AppCompatActivity {

    private AppOpenAdManager appOpenAdManager;

    private int numActivityRestarted = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        appOpenAdManager = ((MyApplication) getApplication()).getAppOpenAdManager();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        numActivityRestarted++;

        if (canShowAppOpenAd()) {
            appOpenAdManager.showAdIfAvailable();
        }
    }

    private boolean canShowAppOpenAd() {
        return numActivityRestarted % 3 == 0;
    }
}

onRestart() 콜백 메서드는 액티비티가 백그라운드 상태로 전환되었다 다시 활성 상태로 전환될 때 호출됩니다. 따라서 이를 활용하면 앱 복귀 시점에 앱 오프닝 광고를 자연스럽게 보여줄 수 있습니다.

실행 및 테스트

이것으로 앱 구현이 모두 끝났습니다. 최초 실행시에는 광고가 로드되지 않은 상태이기에 광고가 표시되지 않지만, 이후에 앱을 다시 실행하면 앱 실행 하면에서 광고가 표시되는 것을 확인할 수 있습니다.

사용중이던 앱으로 다시 복귀하는 경우에도 앱 오프닝 광고가 잘 표시되는 것을 확인할 수 있습니다.

추가 리소스

앞서 살펴본 예제에서 사용한 AppOpenAdManager 클래스는 예제 수준에서 사용하기에는 큰 문제가 없지만, 확장성이 그리 좋지 않습니다. 여러 설정을 조금 더 입맛에 맞게 조정하여 사용하고 싶으신 분들은 아래 버전의 AppOpenAdManager 클래스를 사용하세요.

package com.androidhuman.ads.appopenads;

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.LoadAdError;
import com.google.android.gms.ads.appopen.AppOpenAd;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class AppOpenAdManager extends AppOpenAd.AppOpenAdLoadCallback
        implements Application.ActivityLifecycleCallbacks {

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
            AppOpenAd.APP_OPEN_AD_ORIENTATION_LANDSCAPE})
    public @interface AdOrientation {

    }

    @Retention(RetentionPolicy.SOURCE)
    @IntRange(from = 0L, to = MAX_AD_EXPIRY_DURATION)
    public @interface AdExpiryDuration {

    }

    public static class Builder {

        private final Application application;

        private final String adUnitId;

        @AdOrientation
        private int orientation = AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT;

        @AdExpiryDuration
        private long adExpiryDuration = MAX_AD_EXPIRY_DURATION;

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

        public Builder(@NonNull Application application, @NonNull String adUnitId) {
            this.application = application;
            this.adUnitId = adUnitId;
        }

        public Builder setOrientation(@AdOrientation int orientation) {
            this.orientation = orientation;
            return this;
        }

        public Builder setAdExpiryDuration(@AdExpiryDuration long duration) {
            this.adExpiryDuration = duration;
            return this;
        }

        public Builder setAdRequest(@NonNull AdRequest request) {
            this.adRequest = request;
            return this;
        }

        public AppOpenAdManager build() {
            return new AppOpenAdManager(this);
        }
    }

    public static final String TEST_AD_UNIT_ID = "ca-app-pub-3940256099942544/1033173712";

    public static final long MAX_AD_EXPIRY_DURATION = 3600000 * 4;

    private static final String TAG = "AppOpenManager";

    private final Application application;

    private final String adUnitId;

    private final int orientation;

    private final long adExpiryDuration;

    private final AdRequest adRequest;

    private Activity mostCurrentActivity;

    private AppOpenAd ad;

    private boolean isShowingAd = false;

    private long lastAdFetchTime = 0L;

    private AppOpenAdManager(Builder builder) {
        this.application = builder.application;
        this.adUnitId = builder.adUnitId;
        this.orientation = builder.orientation;
        this.adExpiryDuration = builder.adExpiryDuration;
        this.adRequest = builder.adRequest;

        // Used to keep track of most recent activity.
        this.application.registerActivityLifecycleCallbacks(this);
    }

    public void showAdIfAvailable() {
        showAdIfAvailable(null);
    }

    public void showAdIfAvailable(@Nullable final FullScreenContentCallback listener) {
        if (this.isShowingAd) {
            Log.e(TAG, "Can't show the ad: Already showing the ad");
            return;
        }

        if (!isAdAvailable()) {
            Log.d(TAG, "Can't show the ad: Ad not available");
            fetchAd();
            return;
        }

        FullScreenContentCallback callback = new FullScreenContentCallback() {
            @Override
            public void onAdFailedToShowFullScreenContent(AdError error) {
                if (listener != null) {
                    listener.onAdFailedToShowFullScreenContent(error);
                }
            }

            @Override
            public void onAdShowedFullScreenContent() {
                if (listener != null) {
                    listener.onAdShowedFullScreenContent();
                }
                AppOpenAdManager.this.isShowingAd = true;
            }

            @Override
            public void onAdDismissedFullScreenContent() {
                if (listener != null) {
                    listener.onAdDismissedFullScreenContent();
                }
                isShowingAd = false;
                AppOpenAdManager.this.ad = null;
                fetchAd();
            }
        };

        ad.show(mostCurrentActivity, callback);
    }

    private void fetchAd() {
        if (isAdAvailable()) {
            return;
        }

        AppOpenAd.load(application, adUnitId, adRequest, orientation, this);
    }

    private boolean isAdAvailable() {
        return this.ad != null && !isAdExpired();
    }

    private boolean isAdExpired() {
        return System.currentTimeMillis() - lastAdFetchTime > adExpiryDuration;
    }

    // AppOpenAd.AppOpenAdLoadCallback implementations

    @Override
    public void onAppOpenAdLoaded(AppOpenAd ad) {
        Log.d(TAG, "Ad loaded");
        this.lastAdFetchTime = System.currentTimeMillis();
        this.ad = ad;
    }

    @Override
    public void onAppOpenAdFailedToLoad(LoadAdError error) {
        Log.d(TAG, "Failed to load an ad: " + error.getMessage());
    }

    // Application.ActivityLifecycleCallbacks implementations

    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        // Do nothing
    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {
        // Do nothing
    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {
        this.mostCurrentActivity = activity;
    }

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

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

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
        // Do nothing
    }

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

이 글에서 작성한 예제 프로젝트 소스 코드는 Github 내 kunny/blog_samples 저장소에서 확인할 수 있습니다.

  1. 유효 시간은 추후 변경될 수 있습니다. 개발자 문서 

kunny

커니

안드로이드와 오픈소스, 코틀린(Kotlin)에 관심이 많습니다. 한국 GDG 안드로이드 운영자 및 GDE 안드로이드로 활동했으며, 현재 구글에서 애드몹 기술 지원을 담당하고 있습니다.

Read More