要在 Capacitor 应用中处理 AdMob GDPR 同意机制,请使用 Google 的用户消息平台(UMP)SDK:每次启动时请求同意信息,如有需要则显示同意表单,并且仅在 canRequestAds 为 true 时初始化广告。您还需要一个隐私选项入口,以便用户更改其选择;在 iOS 上,UMP 流程应在 App Tracking Transparency 提示之前进行。本指南展示了两种配置的完整流程:一个将 UMP 封装在 JavaScript 中的插件,以及 @capgo/capacitor-admob 一个小型的原生同意桥接。
UMP 的作用以及为什么 AdMob 需要它
自 2024 年 1 月起,Google 要求向 EEA、英国和瑞士用户展示广告的发布者使用与 IAB 透明度和同意框架 (TCF) 集成的 Google 认证同意管理平台 (CMP)。UMP SDK 是 Google 自有的认证 CMP,免费且内置于 AdMob 控制台中。
UMP 为您完成以下三项工作:
- 检测用户是否位于需要获取同意的地区。
- 以用户语言显示您在 AdMob 中配置的同意消息。
- 将用户的选择作为 TC 字符串存储在设备上(标准
IABTCF_键位于UserDefaults(iOS 端)以及SharedPreferencesAndroid 上),Google Mobile Ads SDK 和中介合作伙伴在请求广告时会读取此字符串。
您还可以使用 UMP 处理美国州隐私法规,AdMob 将其称为“US states”消息,同样可在该控制台中配置。
第 1 步:在 AdMob 中创建消息
在 AdMob 控制台中,前往 隐私与消息:
- 创建一个 欧洲法规 消息(GDPR)。选择其适用的应用、语言以及您的广告合作伙伴列表。
- 可选地创建一个 美国各州 消息。
- 在 iOS 上,创建一个 IDFA 说明 消息,前提是你计划请求 App Tracking Transparency。UMP 会在系统 ATT 提示前显示它。
- 发布消息。未发布的消息不会显示。
如果之后表单始终没出现在应用里,首先要检查的就是消息是否未发布或未分配。
第 2 步:确定操作顺序
适用于大多数应用的顺序:
- 应用启动。
- 使用 UMP 请求同意信息更新(每次启动)。
- 如果需要,加载并显示同意表单。
- 如果
canRequestAds如果为 true,则启动 Mobile Ads SDK 并加载广告。 - 当 UMP 表示需要隐私选项时,在设置界面中显示“隐私设置”入口。
Google 也允许在请求同意时并行启动 Mobile Ads SDK,前提是 canRequestAds 在之前的会话中已为 true。这能缩短回访用户看到首条广告的时间。
方案 A:一个用 JavaScript 封装 UMP 的 plugin
@capacitor-community/admob (版本 8 支持 Capacitor 8)直接暴露 UMP 方法: requestConsentInfo, showConsentForm, showPrivacyOptionsForm 以及 resetConsentInfo.
bun add @capacitor-community/admob
bunx cap sync
import {
AdMob,
AdmobConsentStatus,
AdmobConsentDebugGeography,
} from '@capacitor-community/admob';
export async function initAdsWithConsent(isDebug: boolean) {
await AdMob.initialize();
let info = await AdMob.requestConsentInfo(
isDebug
? {
debugGeography: AdmobConsentDebugGeography.EEA,
testDeviceIdentifiers: ['YOUR_TEST_DEVICE_ID'],
}
: undefined,
);
if (info.isConsentFormAvailable && info.status === AdmobConsentStatus.REQUIRED) {
info = await AdMob.showConsentForm();
}
// The enum value is the string 'REQUIRED'
const showPrivacyEntry = String(info.privacyOptionsRequirementStatus) === 'REQUIRED';
return { canRequestAds: info.canRequestAds, showPrivacyEntry };
}
export async function openPrivacyOptions() {
await AdMob.showPrivacyOptionsForm();
}
此插件是上述顺序的唯一例外:其 README 要求 AdMob.initialize() 在……之前 requestConsentInfo(),且在 iOS 上,在 initialize() 运行之前无法显示同意表单。 initialize() 会启动 SDK,但不会请求广告,因此关键规则是:在 canRequestAds 为 true 之前,绝不加载广告。如果需要 SDK 在用户回答之前保持停止状态,请使用选项 B,即同意流程在 AdMob.start().
查看 canRequestAds 之前运行,而不仅仅是在启动时,因为用户可以从隐私选项表单中撤回同意。
方案 B:@capgo/capacitor-admob 搭配原生 UMP bridge
@capgo/capacitor-admob 为您提供 AdMob.start()、请求配置、横幅、插屏、激励视频和激励插屏广告,以及 iOS 跟踪授权。它不封装 UMP,因此您需要添加一个小型应用本地插件,在调用 AdMob.start().
bun add @capgo/capacitor-admob
bunx cap sync
添加 UMP SDK
- iOS: 添加 Google 的
GoogleUserMessagingPlatform包,使用 Swift Package Manager(Capacitor 8 个项目默认使用 SPM),或者如果您的项目仍在使用 CocoaPods,则使用同名的 CocoaPods pod。 - Android: 中的
android/app/build.gradle:
dependencies {
implementation "com.google.android.ump:user-messaging-platform:<latest version>"
}
使用 Google Maven 仓库中的最新版本。
iOS 同意插件
创建 ios/App/App/ConsentPlugin.swift:
import Capacitor
import UserMessagingPlatform
@objc(ConsentPlugin)
public class ConsentPlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "ConsentPlugin"
public let jsName = "Consent"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "gather", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "showPrivacyOptions", returnType: CAPPluginReturnPromise),
]
@objc func gather(_ call: CAPPluginCall) {
let parameters = RequestParameters()
DispatchQueue.main.async {
ConsentInformation.shared.requestConsentInfoUpdate(with: parameters) { error in
if let error = error {
call.reject(error.localizedDescription)
return
}
guard let vc = self.bridge?.viewController else {
call.reject("No view controller")
return
}
ConsentForm.loadAndPresentIfRequired(from: vc) { formError in
if let formError = formError {
call.reject(formError.localizedDescription)
return
}
call.resolve(self.state())
}
}
}
}
@objc func showPrivacyOptions(_ call: CAPPluginCall) {
DispatchQueue.main.async {
guard let vc = self.bridge?.viewController else {
call.reject("No view controller")
return
}
ConsentForm.presentPrivacyOptionsForm(from: vc) { formError in
if let formError = formError {
call.reject(formError.localizedDescription)
return
}
call.resolve(self.state())
}
}
}
private func state() -> [String: Any] {
return [
"canRequestAds": ConsentInformation.shared.canRequestAds,
"privacyOptionsRequired":
ConsentInformation.shared.privacyOptionsRequirementStatus == .required,
]
}
}
从 CAPBridgeViewController 子类注册它,并在 view controller 上设置该类,在 Main.storyboard:
import Capacitor
class MainViewController: CAPBridgeViewController {
override open func capacitorDidLoad() {
bridge?.registerPluginInstance(ConsentPlugin())
}
}
Android 同意插件
创建 android/app/src/main/java/com/example/app/ConsentPlugin.java 中(使用你的包名):
package com.example.app;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.google.android.ump.ConsentInformation;
import com.google.android.ump.ConsentRequestParameters;
import com.google.android.ump.UserMessagingPlatform;
@CapacitorPlugin(name = "Consent")
public class ConsentPlugin extends Plugin {
@PluginMethod
public void gather(PluginCall call) {
ConsentInformation info = UserMessagingPlatform.getConsentInformation(getContext());
ConsentRequestParameters params = new ConsentRequestParameters.Builder().build();
getActivity().runOnUiThread(() ->
info.requestConsentInfoUpdate(
getActivity(),
params,
() -> UserMessagingPlatform.loadAndShowConsentFormIfRequired(getActivity(), formError -> {
if (formError != null) {
call.reject(formError.getMessage());
return;
}
call.resolve(state(info));
}),
requestError -> call.reject(requestError.getMessage())
)
);
}
@PluginMethod
public void showPrivacyOptions(PluginCall call) {
getActivity().runOnUiThread(() ->
UserMessagingPlatform.showPrivacyOptionsForm(getActivity(), formError -> {
if (formError != null) {
call.reject(formError.getMessage());
return;
}
call.resolve(state(UserMessagingPlatform.getConsentInformation(getContext())));
})
);
}
private JSObject state(ConsentInformation info) {
JSObject ret = new JSObject();
ret.put("canRequestAds", info.canRequestAds());
ret.put(
"privacyOptionsRequired",
info.getPrivacyOptionsRequirementStatus() ==
ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED
);
return ret;
}
}
在 MainActivity.java 在……之前 super.onCreate:
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(ConsentPlugin.class);
super.onCreate(savedInstanceState);
}
}
通过 TypeScript 调用
import { registerPlugin } from '@capacitor/core';
import { AdMob, BannerAd } from '@capgo/capacitor-admob';
interface ConsentState {
canRequestAds: boolean;
privacyOptionsRequired: boolean;
}
interface ConsentPlugin {
gather(): Promise<ConsentState>;
showPrivacyOptions(): Promise<ConsentState>;
}
const Consent = registerPlugin<ConsentPlugin>('Consent');
export async function startAds() {
let state: ConsentState;
try {
state = await Consent.gather();
} catch (err) {
console.warn('Consent flow failed', err);
return { showPrivacyEntry: false };
}
if (state.canRequestAds) {
await AdMob.start();
const banner = new BannerAd({
adUnitId: 'ca-app-pub-3940256099942544/2934735716', // Google test banner (iOS)
position: 'bottom',
});
await banner.show();
}
return { showPrivacyEntry: state.privacyOptionsRequired };
}
export const openPrivacyOptions = () => Consent.showPrivacyOptions();
Native code 的更改需要新的商店构建。之后,设置屏幕中的同意文案和广告位逻辑可以随 Capgo 实时更新.
步骤 3:添加隐私选项入口
当 UMP 报告需要隐私选项时,Google 要求提供一种可见的方式来重新打开同意选项。在设置页面中添加一行“隐私设置”,不需要时将其隐藏:
const { showPrivacyEntry } = await startAds();
document.querySelector('#privacy-row')?.toggleAttribute('hidden', !showPrivacyEntry);
document.querySelector('#privacy-row')?.addEventListener('click', () => openPrivacyOptions());
用户更改选择后,请在下一次广告请求前重新读取 canRequestAds 。
第 4 步:iOS 上的 App Tracking Transparency
ATT 与 GDPR 是两个不同的概念。ATT 用于控制 iOS 上的 IDFA 访问权限,而 GDPR 同意则控制是否以及如何处理个人数据。对于 iOS 上的欧盟用户,你通常两者都需要。
- 添加
NSUserTrackingUsageDescription改为Info.plist. - 如果你在 AdMob 中配置了相关消息,可以让 UMP 显示 IDFA 说明消息并触发 ATT。否则,请在同意流程结束后自行请求 ATT。
使用 @capgo/capacitor-admob 你可以直接读取并请求 ATT 状态:
import { AdMob, TrackingAuthorizationStatus } from '@capgo/capacitor-admob';
const { status } = await AdMob.trackingAuthorizationStatus();
if (status === TrackingAuthorizationStatus.notDetermined) {
await AdMob.requestTrackingAuthorization();
}
Capgo 也有一个独立的 App Tracking Transparency 插件 如果你需要为其他 SDK 获取 ATT 权限。别忘了在 privacy manifest 和 App Store privacy labels 中声明追踪行为,请参阅我们的 privacy manifest 指南.
第 5 步:使用 debug geography 进行测试
UMP 只会向它判定位于 EEA、英国或瑞士的用户显示 GDPR 表单。要在其他地区测试:
- 运行一次 App,找到 UMP 输出中包含设备哈希 ID 的日志行。
- 将其添加为测试设备,并将 debug geography 设为 EEA。使用社区插件时,传入
debugGeography以及testDeviceIdentifiers改为requestConsentInfo。在原生桥接中,设置DebugSettings(iOS 端)以及ConsentDebugSettings(Android 端),且仅限 debug build。 - 在每次运行之间重置同意状态(
resetConsentInfo()用于社区插件,ConsentInformation.reset()用于原生方式),以便再次显示表单。
切勿在 release build 中包含 debug 设置。用 build flag 控制它们。
步骤 6:处理错误,不阻塞应用
同意请求可能失败:没有网络、应用 ID 配置错误,或者没有已发布的消息。把失败当作“暂时不展示广告”,而不是崩溃:
- 如果
requestConsentInfoUpdate失败,但canRequestAds如果该值在上一次会话中为 true,你仍然可以请求广告。 - 如果表单加载失败,请记录日志并在下次启动时重试。
- 同意表单显示在屏幕上时,切勿展示广告。
故障排除
表单始终不显示。 消息未发布、未分配给此应用 ID,或您位于 EEA 之外且未启用调试地理位置。请检查 AdMob 应用 ID 是否与 Info.plist 以及 AndroidManifest.xml 控制台中的 ID 一致。
表单每次启动都会显示。 用户没做选择就关闭了,或者你在生产环境 code 中调用了 reset。
canRequestAds 对美国用户返回 false。 你发布了美国州级消息,而用户选择不出售或共享数据。只能通过 SDK 投放受限广告,不要绕过它。
中介合作伙伴未获得同意。 请确保每个合作伙伴 adapter 都是最新版本,并已列入 GDPR 消息的广告合作伙伴列表。
iOS ATT prompt 始终不出现。 NSUserTrackingUsageDescription 缺失,或者用户在设置中关闭了“允许 App 请求跟踪”。
不止于表单的合规
UMP 负责广告侧。你的 App 仍需提供隐私政策,列出 AdMob、数据保留规则以及处理数据请求的方式。我们的 GDPR 合规清单 涵盖 App 的其余部分。