ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-06-17上級

ダイアログが重なる前に止める — 課金・レビュー誘導・リワード広告を1か所のゲートで束ねる

ペイウォール・レビュー誘導・リワード広告が同じ瞬間に重なって出る不具合を、優先度つきの中央ゲートで根治した実装記録です。Antigravityのエージェントに散らばったshow()呼び出しの掃き出しを任せ、表示ポリシーは自分で握る線引きで進めました。

antigravity369android19monetization11uxkotlin5

プレミアム記事

リワード広告を見終えた直後に、レビュー誘導のダイアログとペイウォールが同時に飛び出してきたことがあります。自分が運営している壁紙アプリ(iOS / Android 合わせて累計5,000万DL超)で、ある更新の段階公開中に起きました。ユーザーから見れば「広告を見せられた上に、二枚重ねで何かを請求された」ようにしか映りません。

原因は単純で、ダイアログを出す show() がコードのあちこちに散らばっていて、誰も「いま画面に何が出ているか」を知らなかったからです。広告のコールバックがレビュー誘導のタイマーと無関係に発火し、両方が「いま出してよい」と判断してしまう。個々のロジックは正しいのに、合わさると壊れます。

以下では、その衝突を優先度つきの中央ゲート(ModalGate)で根治した実装をたどります。コードは Android(Kotlin)ですが、考え方は SwiftUI でも React Native でも同じです。そして、散らばった呼び出しの掃き出しは Antigravity のエージェントに任せ、どのモーダルを優先するかという判断だけは自分で握る、という線引きについても書きます。

「いま何が出ているか」を誰も知らない状態が根本原因です

モーダルが衝突するアプリには、共通の構造があります。表示の判断が呼び出し側に分散しているのです。

  • ペイウォール: 機能制限に触れた瞬間に PaywallDialog.show()
  • レビュー誘導: 起動回数が閾値を超えたら ReviewInduction.maybeShow()
  • リワード広告: 戻るボタンや特定操作の後に RewardedIntersDialog.show()

それぞれは「自分が出てよい条件」しか見ていません。画面全体で一度に一枚だけ、という不変条件はどこにも書かれていない。だから二枚目が前面の一枚に気づかず重なります。さらに厄介なのは、優先度が暗黙だという点です。本来はペイウォール(課金に直結)を最優先にしたいのに、たまたまレビュー誘導のタイマーが先に発火すると、価値の低いモーダルが価値の高いモーダルの表示機会を食い潰します。

解決の方針は一つです。すべてのモーダルを、単一の調停役を必ず経由させること。直接 show() を呼ばせない。ゲートが「いま一枚出ているか」「次に出すべきはどれか」を一元的に決めます。

中央ゲートの最小実装 — 単一表示の不変条件と優先度

まず核になる ModalGate です。やることは3つだけです。同時に一枚しか出さない、優先度の高いものを先に出す、出し終わったら次を出す。

// 値が大きいほど優先。課金に直結するものを上に置きます
enum class ModalPriority(val weight: Int) {
    PAYWALL(100),          // 機能制限・課金導線
    REWARDED_AD(60),       // リワード広告
    REVIEW_PROMPT(20),     // ストアレビュー誘導
}
 
// 「出したい」という要求を表す。show は実際の表示処理を遅延実行するラムダ
data class ModalRequest(
    val id: String,
    val priority: ModalPriority,
    val show: (onDismiss: () -> Unit) -> Unit,
)
 
object ModalGate {
    private val pending = mutableListOf<ModalRequest>()
    private var active: ModalRequest? = null
    private var canPresent = false   // フォアグラウンドで安全に出せるか
 
    // 呼び出し側はこれだけを使う。直接 show() は禁止
    @MainThread
    fun enqueue(request: ModalRequest) {
        // 同じ種類の二重投入を防ぐ
        if (pending.any { it.id == request.id } || active?.id == request.id) return
        pending.add(request)
        pump()
    }
 
    @MainThread
    fun setPresentable(value: Boolean) {
        canPresent = value
        if (value) pump()
    }
 
    private fun pump() {
        if (!canPresent || active != null) return
        val next = pending.maxByOrNull { it.priority.weight } ?: return
        pending.remove(next)
        active = next
        next.show { onDismissed(next) }
    }
 
    private fun onDismissed(request: ModalRequest) {
        if (active?.id == request.id) active = null
        pump()  // 次の一枚へ
    }
}

呼び出し側はこう変わります。show() を直接叩くのをやめ、要求をゲートに積むだけにします。

// Before: 各所が勝手に表示していた
PaywallDialog(activity).show()
 
// After: ゲート経由。出すかどうか・いつ出すかはゲートが決める
ModalGate.enqueue(
    ModalRequest(id = "paywall", priority = ModalPriority.PAYWALL) { onDismiss ->
        PaywallDialog(activity).apply { setOnDismissListener { onDismiss() } }.show()
    }
)

これで「同時に二枚」は構造的に起きなくなります。レビュー誘導とペイウォールが同じ瞬間に積まれても、ゲートは weight の高いペイウォールを先に出し、閉じてからレビュー誘導を出すか判断します。優先度をコードに明示した時点で、暗黙の取り合いは消えます。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
レビュー誘導の直後にペイウォールが二重表示されるような「モーダルの衝突」を、優先度つきの中央ゲートで今日から1か所に集約できる
広告SDKの非同期コールバックやActivityのライフサイクルでゲートが破れる3つの罠と、その回避コードを自分のアプリに移植できる
散らばったshow()呼び出しの掃き出しをエージェントに任せ、表示ポリシーの判断は人が握る、という線引きの引き方を持ち帰れる
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-04-05
Antigravity × Compose Multiplatform 実装ガイド:UI共有からDesktop対応まで、統合クロスプラットフォーム開発2026
Compose Multiplatform(CMP)と Antigravity AI IDE を組み合わせ、iOS・Android・Desktop のUI層を共有する高品質クロスプラットフォームアプリを構築する実践ガイド。設計パターン・Antigravity活用・自動テスト・リリースフローまでを網羅。
アプリ開発2026-03-29
Antigravity × Jetpack Compose で Android UI を AI アシスト開発する実践ガイド
Antigravity IDE と Jetpack Compose を組み合わせた Android UI 開発の実践手法を解説。AI によるコンポーネント生成、プレビュー活用、テーマ設計まで具体的なコード例とともに紹介します。
アプリ開発2026-05-17
課金状態の「正」はどこにある? — Antigravityで設計するad-free Source of Truthパターン
累計5,000万DLアプリ開発で実証した課金状態管理の設計パターン。BillingManager・AdFreeManager・ModalGateの3層設計をAntigravityで実装する実践ガイド。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →