メインコンテンツまでスキップ

【Android】App Open Ads(アプリ起動時広告)

App Open Ads(アプリ起動時広告)は、アプリの読み込み画面やバックグラウンドからの復帰画面を収益化するための全画面広告フォーマットです。ユーザーがアプリを起動したタイミングや、フォアグラウンドに切り替えたタイミングで表示されます。ユーザーはいつでも広告を閉じることができます。

実装は以下の3ステップで行います。実運用では加えて、アプリのライフサイクル統合(後述の実装方針アプリのライフサイクルに統合する)が必要です。

  1. 広告を事前にロードする
  2. アプリのライフサイクルイベントを監視し、適切なタイミングで広告を表示する
  3. リスナーでコールバックを処理する

前提条件

  • VAMP SDK v5.3.5 以降(VAMP 全体の最小要件)
  • VAMPPangleAdapter 7.9.10200(Pangle SDK 7.9.1.2 同梱)以降
  • 対応アドネットワーク: 現時点では Pangle
  • androidx.lifecycle:lifecycle-process 2.5.1 以降(バックグラウンド復帰検知に使用)
dependencies {
implementation 'androidx.lifecycle:lifecycle-process:2.5.1'
}

各メソッドの仕様やオプションについては、Androidリファレンスをご参照ください。 テスト時の広告枠IDについてはテストを参照してください。

現時点の Android App Open Ads は、VAMP SDK + VAMPPangleAdapter の組み合わせでご利用ください。導入方法そのものは、以下のページを参照してください。

実装方針

安定した実装には、広告のロードに加えてアプリのライフサイクル制御が重要です。いつロードし、いつ表示し、いつ再ロードするか をアプリ側でも明確に管理してください。

3 つのシナリオで考える

App Open Ads の挙動は、以下の 3 シナリオで考えると整理しやすくなります。各シナリオの行動方針は、AppOpenAdManager 例(後述)の動作と一致します。

  1. 初回起動: ロード済み広告のキャッシュがない状態でアプリが起動する。最初の ActivityonResume に遷移し currentActivity が確定したタイミングで load を開始し、onReceived を受けた時点でそのまま表示する。
  2. バックグラウンド復帰: ロード済み広告がキャッシュにある場合のみ即表示する。未準備の場合は、その復帰時には表示せず次回用の load だけ行う(後追い表示はしない)。
  3. 広告 close 後の再ロード: onClosed / onFailedToShow の直後ではなく、次の onActivityResumed() で安全に再ロードする。理由は「close 直後の preload について」を参照。

加えて、上記いずれのシナリオでも 広告表示中は追加の load / show を行わない ことが前提です。

必要な状態フラグ

Android では、ProcessLifecycleOwneronStart() は初回起動時にもバックグラウンドからの復帰時にも発火します。さらに、onStart() のタイミングでは最前面の Activity がまだ currentActivity として確定していない場合があります。状態を持たずに onStart() のたびに単純に load / show を呼ぶ実装にすると、空振りや二重ロードが発生しやすくなります。

そのため、以下の状態を持つ構成にすると実装しやすくなります。シナリオとの対応関係も併記します。

  • isInitialLaunch
    • 初回起動時のみ、ロード完了後に即表示するためのフラグ(シナリオ 1)
  • isLoadingAd
    • 同じ placementID に対する二重ロードを防ぐためのフラグ(シナリオ 1・2 共通)
  • isShowingAd
    • 広告表示中の onStart() / onActivityResumed() を無視するためのフラグ(シナリオ 2 中の表示完走を守る)
  • showAfterCurrentLoad
    • runForegroundActionisInitialLaunch を即 false 化したうえで、「この load 完了後に show する意図」だけを onReceived まで持ち越すためのフラグ(シナリオ 1 と 2 を分離するための一時保持)
  • pendingForegroundAction
    • onStart() 時に currentActivity が未確定でも、次の onActivityResumed() で foreground 方針を 1 回だけ適用するためのフラグ
  • pendingPreloadAfterResume
    • 広告 close 直後の即時 preload を避け、次回 onActivityResumed() で安全に preload するためのフラグ(シナリオ 3)

上記を使っておくと、初回起動・バックグラウンド復帰・広告クローズ後の再ロードを安全に制御しやすくなります。

広告の読み込み

注記

このセクションと「広告の表示」は API レベルの呼び出し例です。実運用では「アプリのライフサイクルに統合する」で示すマネージャークラス構成(AppOpenAdManager)の採用を推奨します。本セクションだけを実装してしまうと、バックグラウンド復帰時の表示・close 後の再ロード・初回起動と復帰の挙動差をアプリ側で制御できません。

次のコードスニペットでは、広告の読み込みをする場合の実装例です。

リスナーにはVAMPAppOpenAdLoadListenerを設定してください。

AdGeneration管理画面で発行された広告枠IDを*****に設定します。

import jp.supership.vamp.VAMPAppOpenAd;
import jp.supership.vamp.VAMPAppOpenAdLoadListener;
import jp.supership.vamp.VAMPError;
import jp.supership.vamp.VAMPRequest;

public class MainActivity extends AppCompatActivity {
private static final String PLACEMENT_ID = "*****"; // 広告枠IDを設定してください

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

VAMPRequest request = new VAMPRequest.Builder().build();
VAMPAppOpenAd.load(this, PLACEMENT_ID, request,
new VAMPAppOpenAdLoadListener() {
@Override
public void onReceived(@NonNull String placementId) {
// ロード成功。VAMPAppOpenAd.of(placementId) でキャッシュを取得して show() できます
}

@Override
public void onFailedToLoad(@NonNull String placementId,
@NonNull VAMPError error) {
// 広告が取得できませんでした
}
});
}
}

広告の読み込みに成功した場合、VAMPAppOpenAdLoadListeneronReceivedが呼ばれます。

:::note 早期ロードの推奨 App Open Ads はアプリ起動の早い段階で読み込みを開始し、バックグラウンド復帰時にすぐ表示できるようキャッシュしておくことを推奨します。 :::

:::note 重複ロードに注意 load を呼ぶたびに必ず新規ネットワークリクエストが発生する前提で実装しないでください。同一 placementID に対してロード中・表示中・ロード済みの広告がある場合、SDK 側でリクエストはスキップされます。アプリ側でも「表示可能な広告があるか」と「今ロード中か」を管理し、不要な重複ロードを避けることを推奨します。 :::

広告読み込み時のリスナー

広告読み込み時の通知を受け取るためのVAMPAppOpenAdLoadListenerのメソッドについて解説します。

このリスナーはVAMPAppOpenAd.load()(広告読み込み時)で設定します。

広告表示準備完了

広告のロードが成功したタイミングで呼び出されます。

@Override
public void onReceived(@NonNull String placementId)

広告読み込み失敗

広告の読み込みに失敗した際に呼び出されます。

@Override
public void onFailedToLoad(@NonNull String placementId, @NonNull VAMPError error)

詳細についてはエラーコード一覧を参照してください。

期限切れ通知について

Android の VAMPAppOpenAdLoadListener には、ロード済み広告の有効期限切れを通知する専用のコールバックは定義されていません。期限切れの取扱いと再ロード方針は「注意事項 > 広告の有効期限」に集約しています。

広告の表示

VAMPAppOpenAd.of(PLACEMENT_ID) は、その placementID に対してロード済みでまだ表示していない広告のキャッシュを返します。キャッシュがない、または既に表示済み・ロード中の場合は null を返します。

リスナーにはVAMPAppOpenAdListenerを実装したインスタンスを渡してください。

VAMPAppOpenAd appOpenAd = VAMPAppOpenAd.of(PLACEMENT_ID);

if (appOpenAd != null) {
// 広告の表示(listener は VAMPAppOpenAdListener を実装したインスタンス)
appOpenAd.show(this, new VAMPAppOpenAdListener() {
@Override
public void onOpened(@NonNull String placementId) { /* 広告表示開始 */ }

@Override
public void onClosed(@NonNull String placementId, boolean adClicked) { /* 広告クローズ */ }

@Override
public void onFailedToShow(@NonNull String placementId, @NonNull VAMPError error) { /* 表示失敗 */ }
});
}

アプリのライフサイクルに統合する

バックグラウンド復帰時の表示

App Open Adsの主なユースケースは、アプリがバックグラウンドからフォアグラウンドに復帰したタイミングでの広告表示です。ProcessLifecycleOwner を使用してアプリのフォアグラウンド復帰を検知し、Application.ActivityLifecycleCallbacks で最前面の Activity を追跡します。

以下は Application クラスで初期化するマネージャークラスの実装例です。DefaultLifecycleObserveronStart() でフォアグラウンド復帰を検知し、onActivityResumed()Activity が確定したタイミングに表示・ロードを行う構成にしています。onStart() 時点で currentActivity が未確定な場合は、フラグだけ立てて次の onActivityResumed() で処理を再開します。

広告 close 直後の即時 preload は不安定になりやすいため、次の onActivityResumed() で preload する構成にしています。

import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import jp.supership.vamp.VAMPAppOpenAd
import jp.supership.vamp.VAMPAppOpenAdListener
import jp.supership.vamp.VAMPAppOpenAdLoadListener
import jp.supership.vamp.VAMPError
import jp.supership.vamp.VAMPRequest

class AppOpenAdManager(application: Application) :
DefaultLifecycleObserver,
Application.ActivityLifecycleCallbacks {

companion object {
private const val PLACEMENT_ID = "*****" // 広告枠IDを設定してください
}

private var currentActivity: Activity? = null
private var isInitialLaunch = true
private var isLoadingAd = false
private var isShowingAd = false
private var showAfterCurrentLoad = false

// onStart 時点で Activity が未確定でも、resume 後に foreground 方針を 1 回だけ適用するためのフラグ。
private var pendingForegroundAction = false

// close / show failure 直後の不安定な再 load を避け、次回 resume で preload するためのフラグ。
private var pendingPreloadAfterResume = false

init {
application.registerActivityLifecycleCallbacks(this)
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}

override fun onStart(owner: LifecycleOwner) {
pendingForegroundAction = true
runForegroundAction()
}

override fun onStop(owner: LifecycleOwner) {
// background へ移った場合は、load 完了後の即表示要求を取り下げる
showAfterCurrentLoad = false
pendingForegroundAction = false
}

private fun runForegroundAction() {
if (!pendingForegroundAction || isShowingAd) return
if (currentActivity == null) return

if (showAdIfAvailable()) return

val showAfterLoad = isInitialLaunch
isInitialLaunch = false
pendingForegroundAction = false

// 初回起動時のみロード完了後の即表示を許可。
// 復帰時に未準備だった場合は、次回用に load のみ実行する。
loadAd(showAfterLoad = showAfterLoad)
}

private fun loadAd(showAfterLoad: Boolean) {
if (isShowingAd || isLoadingAd) return

if (VAMPAppOpenAd.of(PLACEMENT_ID) != null) return

val activity = currentActivity ?: return

isLoadingAd = true
showAfterCurrentLoad = showAfterLoad

val request = VAMPRequest.Builder().build()
VAMPAppOpenAd.load(activity, PLACEMENT_ID, request,
object : VAMPAppOpenAdLoadListener {
override fun onReceived(placementId: String) {
isLoadingAd = false
val shouldShow = showAfterCurrentLoad
showAfterCurrentLoad = false
if (shouldShow) {
showAdIfAvailable()
}
}

override fun onFailedToLoad(placementId: String, error: VAMPError) {
isLoadingAd = false
showAfterCurrentLoad = false
// load 失敗後は後追い表示せず、次回 foreground に任せる。
pendingForegroundAction = false
}
})
}

private fun showAdIfAvailable(): Boolean {
if (isShowingAd) return true

val activity = currentActivity ?: run {
// 表示先 Activity 未確定なら次回 resume で再評価する。
pendingForegroundAction = true
return false
}

val appOpenAd = VAMPAppOpenAd.of(PLACEMENT_ID) ?: return false

pendingPreloadAfterResume = false
pendingForegroundAction = false
isInitialLaunch = false
isShowingAd = true

appOpenAd.show(activity, object : VAMPAppOpenAdListener {
override fun onOpened(placementId: String) {
// 広告表示開始
}

override fun onClosed(placementId: String, adClicked: Boolean) {
isShowingAd = false
// close 直後ではなく、次回 resume で preload する。
pendingPreloadAfterResume = true
}

override fun onFailedToShow(placementId: String, error: VAMPError) {
isShowingAd = false
pendingPreloadAfterResume = true
}
})
return true
}

private fun runDeferredActions() {
if (pendingForegroundAction && !isShowingAd) {
runForegroundAction()
return
}

// 広告 close 直後ではなく、次に通常画面へ戻ったタイミングで preload する。
if (pendingPreloadAfterResume && !isLoadingAd &&
VAMPAppOpenAd.of(PLACEMENT_ID) == null
) {
pendingPreloadAfterResume = false
loadAd(showAfterLoad = false)
}
}

// --- ActivityLifecycleCallbacks ---
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
runDeferredActions()
}

override fun onActivityPaused(activity: Activity) {
// 広告表示中は currentActivity を保持し、close 後に元の Activity へ戻れるようにする
if (!isShowingAd && currentActivity == activity) {
currentActivity = null
}
}

override fun onActivityDestroyed(activity: Activity) {
if (currentActivity == activity) {
currentActivity = null
}
}

override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
override fun onActivityStarted(activity: Activity) = Unit
override fun onActivityStopped(activity: Activity) = Unit
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
}

Application クラスの onCreate で初期化します。AndroidManifest.xmlandroid:name を設定してください。

class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
AppOpenAdManager(this)
}
}
<!-- AndroidManifest.xml -->
<application
android:name=".MyApplication"
...>

:::note Note ProcessLifecycleOwneronStart() は初回起動時にも呼ばれます。初回起動時は VAMPAppOpenAd.of()null を返すため、上記マネージャーは初回起動時のみロード完了後の即表示を許可する構成にしています。 :::

初回起動時の表示方針

初回起動時は、ロード済み広告のキャッシュがまだ存在しません。そのため、以下のように考えてください。

  • 最初の ActivityonResume に遷移し、currentActivity が確定したタイミングで load を開始する(上記サンプルの AppOpenAdManager はこの方針で動作します)
  • 初回起動時のみ、onReceived を受けたタイミングで即 show する
  • 初回 load に失敗した場合は、その起動セッションでは表示できない可能性がある

初回起動時に「まだ広告がないから即座に再ロードする」「一定時間後に強制表示する」といった制御は推奨しません。App Open Ads は起動直後の自然な表示を想定したフォーマットのため、広告未準備のまま起動が進んだ場合は、そのセッションでは通常画面へ遷移してください。

コールドスタート時の表示

アプリの初回起動時(Cold Start)に広告を表示する場合、SDK初期化と広告ロードが完了するまで時間がかかる可能性があります。初回起動時はロード済み広告のキャッシュがないため、広告が表示されないことがあります。

初回起動時に広告を表示する必要がある場合は、ロード完了のリスナー(onReceived)を受け取ったタイミングで表示してください。

広告表示時のリスナー

VAMPAppOpenAdListener のメソッドについて解説します。

このリスナーはVAMPAppOpenAdshow()(広告表示時)で設定します。

広告表示開始

広告の表示が開始されたタイミングで呼び出されます。

@Override
public void onOpened(@NonNull String placementId)

広告を閉じる

広告を閉じたタイミングで呼び出されます。adClicked には広告がクリックされたかどうかが渡されます。

@Override
public void onClosed(@NonNull String placementId, boolean adClicked)

広告表示失敗

広告の表示に失敗した際に呼び出されます。

@Override
public void onFailedToShow(@NonNull String placementId, @NonNull VAMPError error)

詳細についてはエラーコード一覧を参照してください。

注意事項

広告表示中のロードについて

広告の表示中(onOpened から onClosed が呼ばれるまでの間)は、広告のロードを実行しないでください。SDK 側でロード中・表示中のリクエストはスキップされますが、アプリ側で isShowingAd を管理して load 呼び出し自体を抑止する構成を推奨します。

広告を閉じた後(onClosed)または表示失敗後(onFailedToShow)に再ロードを行ってください。

復帰時の再表示方針

バックグラウンド復帰時は、以下のような挙動にすることを推奨します。

  • 表示可能な広告がある場合: 即表示する
  • 広告が未準備の場合: その場では表示せず、次回用に load だけ行う

このとき、「復帰した瞬間に広告が未準備だった場合、ロード完了後にそのまま後追いで自動表示する」挙動は推奨しません。ユーザーがすでに通常画面で操作を始めている可能性があるためです。

実装上は、上記サンプルのように showAfterCurrentLoadisInitialLaunchtrue の場合だけ true にする構成にし、復帰時の load では後追い表示しないようにしてください。

close 直後の preload について

onClosed / onFailedToShow の直後にそのまま load を呼ぶと、Activity の状態遷移と重なって不安定になる場合があります。上記サンプルでは、onClosed / onFailedToShow では pendingPreloadAfterResume フラグを立てるだけにとどめ、次の onActivityResumed() で安全に再ロードする構成にしています。

アプリ側で管理すべき状態

App Open Ads を安定して組み込むには、SDK の load / show を呼ぶだけでなく、アプリ側でも状態を管理してください。各フラグの役割は「実装方針」を参照してください。

実装者が誤りやすいポイント

  • onStart() のタイミングだけで制御してしまう currentActivity が未確定なまま show を試みて、空振りしやすくなります。onActivityResumed()Activity を取得し、そこから foreground 方針を実行する構成にしてください。
  • 広告表示中の onStart() で再度 load してしまう 状態管理が崩れ、復帰時に広告が表示されなくなる原因になります。
  • 起動直後の UI 準備前や onCreate で別の許諾ダイアログを要求してしまう App Open Ads の表示導線と競合し、広告表示や許諾ダイアログのどちらも空振りする場合があります。広告表示導線と競合しないタイミングで要求してください。
  • 初回起動時とバックグラウンド復帰時を同じロジックで扱ってしまう 初回起動時は onReceived 契機の即表示、復帰時は ready なら表示・未準備なら次回用ロード、という違いを分けて実装してください。
  • 広告を閉じたあとに再ロードしない 次回復帰時に即表示できなくなります。上記サンプルのように次回 onActivityResumed() で preload する構成にしてください。

広告の有効期限

ロード済みの広告にはキャッシュ有効期限があります。有効期限を過ぎた広告を表示した場合、有効なインプレッションとしてカウントされず、収益に影響する可能性があります。

Pangle の有効期限ポリシーは 1 時間です。 1時間を超えた広告表示は有効インプレッションとしてカウントされません。

ただし、現行の Android App Open Ads 実装では、Pangle は有効期限超過を SDK 側で一律に onFailedToShow へ落とす前提にはなっていません。Pangle 連携時は表示失敗だけに依存せず、広告を閉じた後や表示失敗後に必ず再ロードする構成を前提にしてください。

:::note 期限切れコールバックは存在しません Android の VAMPAppOpenAdLoadListener には、ロード済み広告の期限切れを通知する専用のコールバックはありません。 :::

表示頻度の制御

バックグラウンドからの復帰のたびに広告を表示すると、ユーザー体験が悪化する可能性があります。以下のような制御を検討してください。

  • 前回の広告表示からの最低インターバル(例: N分以上経過)
  • 1日あたりの表示回数上限

以下は SharedPreferences に最終表示時刻を保存し、最低インターバル経過前は show をスキップする最小例です。AppOpenAdManagershowAdIfAvailable() 冒頭から呼び出します。

private val prefs = application.getSharedPreferences("app_open_ads", Context.MODE_PRIVATE)
private val minIntervalMs = TimeUnit.MINUTES.toMillis(5)

private fun canShowNow(): Boolean {
val last = prefs.getLong("last_shown_at", 0L)
return System.currentTimeMillis() - last >= minIntervalMs
}

private fun markShown() {
prefs.edit().putLong("last_shown_at", System.currentTimeMillis()).apply()
}

呼び出し側(showAdIfAvailable() 内):

if (!canShowNow()) return false
// ...既存の show 処理...
appOpenAd.show(activity, object : VAMPAppOpenAdListener {
override fun onOpened(placementId: String) { markShown() }
// ...
})

1日あたりの表示回数上限を加える場合は、日付キー(yyyy-MM-dd)ごとのカウンタを SharedPreferences に保持し、canShowNow() の条件に組み込みます。プロセス再起動を跨いだ集計が必要なため、SharedPreferences などの永続化先を使ってください。