Trusted Arbiter

在 React Native 应用中比较 CloudX 出价和受支持的第三方出价

Trusted Arbiter 会比较已加载的 CloudX 出价与受支持的第三方出价,并返回选中的平台。React Native 支持由 CloudX 原生 SDK 提供,支持 CloudX、Unity LevelPlay、PubMatic 和发布商传入的自定义出价输入。

为什么使用 Trusted Arbiter?

当应用从多个平台加载广告时,需要决定展示哪个广告。Trusted Arbiter 为受支持的出价提供统一的比较 API。

减少需要维护的出价比较逻辑

将受支持的出价提交给 Trusted Arbiter,根据返回的平台选择要展示的广告。应用仍需负责合作伙伴 SDK 的集成、广告加载和展示。

使用可用的出价值

在可以获取实际出价值时,Trusted Arbiter 可以使用这些值进行比较。部分广告需求使用估算价格。请参阅下文中各类受支持输入的定价说明。

使用各 SDK 已加载广告对象或广告信息对象来填充出价值:

import { CloudX, CloudXArbiterBid } from 'cloudx-react-native';

// cloudXAdInfo 是 CloudX 加载回调中的 CloudXAdInfo 对象。
// levelPlayAdInfo 是 Unity LevelPlay 广告信息对象。
// pubMaticPrice 和 pubMaticPartnerName 来自 PubMatic/OpenWrap 出价对象。
const result = await CloudX.arbiter({
    bids: [
        CloudXArbiterBid.cloudX(cloudXAdInfo),
        CloudXArbiterBid.levelPlay({
            networkName: levelPlayAdInfo.adNetwork,
            revenue: levelPlayAdInfo.revenue,
            precision: levelPlayAdInfo.precision,
        }),
        CloudXArbiterBid.pubMatic({
            price: pubMaticPrice,
            partnerName: pubMaticPartnerName,
        }),
    ],
});

console.log('Selected platform:', result.platform);

超时、出错或 arbiter 服务不可用时,SDK 会在传入的受支持出价中回退选择可比较美元出价最高的平台。

何时运行仲裁器

请在候选广告加载完成后运行仲裁器——绝不要放在展示路径上。CloudX.arbiter() 是一次网络往返,而点击按钮触发广告展示的用户绝不应该等待它。

全屏格式(插屏、激励视频、应用开屏):提前准备。 并行加载全部候选广告。当它们全部落定(加载成功或失败)后运行仲裁器,并把结果保存下来。到达展示位时,立即展示已保存的获胜方,不发起任何网络请求。广告展示或关闭后、展示失败后,或某个候选广告过期时,再重新走一遍这个流程。

import { CloudX, CloudXAdInfo, CloudXArbiterBid, CloudXInterstitialAd, CloudXArbiterResult } from 'cloudx-react-native';

let nextWinner: CloudXArbiterResult | null = null;

// 候选广告加载完成后立即运行,早于展示位。
const prepareWinner = async (cloudXAdInfo: CloudXAdInfo, adMobAdUnitId: string) => {
    nextWinner = await CloudX.arbiter({
        bids: [CloudXArbiterBid.cloudX(cloudXAdInfo), CloudXArbiterBid.adMob({ adUnitId: adMobAdUnitId })],
    });
};

// 在展示位运行。这里没有网络请求。
const showInterstitial = (cloudXAdUnitId: string) => {
    switch (nextWinner?.platform) {
        case 'CLOUDX':
            CloudXInterstitialAd.showAd(cloudXAdUnitId);
            break;
        case 'ADMOB':
            // 展示你的 AdMob 插屏广告
            break;
        default:
            break; // 没有准备好获胜方;继续流程但不展示广告
    }
    nextWinner = null;
};

如果展示位在获胜方保存完成之前就到达,可以选择不展示广告继续流程,或者展示唯一加载成功的那个候选广告。这是可以接受的降级路径,但绝不应作为主路径。

视图格式(banner、MREC):先仲裁,再渲染。 这里没有用户发起的操作在等待,因此直接在 await CloudX.arbiter() 返回处附加或渲染获胜方是正确做法——仲裁器返回本身就是展示的触发点。任何时候都只能附加获胜方的视图或素材。视图附加规则请参见 Banner 与 MREC。

AdMob 与 Google Ad Manager

CloudX 会将已加载的 CloudX 广告与已加载的 AdMob 或 Google Ad Manager 广告进行比较。AdMob 和 Ad Manager 是两个独立的需求来源,因此二者可以同时参与同一次仲裁。此方式适用于发布商自行管理的聚合配置,包括商业上称为 AdMob Pro 的账户;SDK 不提供单独的 AdMob Pro API。

Google 需求方通常不会在展示前透露已加载广告的价格,因此仲裁器无法获得可与 CloudX 出价比较的 pre-bid 价格。CloudX 会根据你的 Google 历史表现自动为出价定价,因此你无需自行提供价格,也不需要任何 pre-bid 定价 API。下文的收入上报会为这份历史提供数据。参见 AdMob/GAM estimated 定价的工作方式。

如果你的 AdMob 账户能够在 pre-bid 阶段提供展示级收益数据,也可以自行提供该精确价格来代替使用估算值——参见下方使用 pre-bid ILRD 手动输入价格。

将 Google 付费事件回传给 CloudX(必需)

上报 Google 的展示级收入是 Trusted Arbiter 中 AdMob 与 Ad Manager 集成的必需环节,而不是可选的分析功能。当通过仲裁胜出的 AdMob 或 Ad Manager 广告展示之后,请把 Google 的付费事件(paid event)转发到 CloudX SDK。缺少这些事件,CloudX 就无法了解 Google 需求方的实际成交价格,其为后续仲裁提供的估算值也会逐渐变差。

请在你的 Google 付费事件回调中调用 CloudX.reportRevenueData()。收入值是货币单位,不是 micros——react-native-google-mobile-ads 的 event.value 已经是货币单位。

import { CloudX, CloudXRevenuePlatform, CloudXRevenuePrecision } from 'cloudx-react-native';

// event 是 Google 广告的 paid event(react-native-google-mobile-ads 的 onPaid)。
const reportGooglePaidEvent = (event, adFormat, adUnitId) =>
    CloudX.reportRevenueData({
        platform: CloudXRevenuePlatform.ADMOB, // Ad Manager 请使用 CloudXRevenuePlatform.GAM
        revenue: event.value,
        adFormat,
        currencyCode: event.currency,
        precision: CloudXRevenuePrecision.ESTIMATED,
        networkName: 'admob',
        adUnitId,
    });

请上报你传给仲裁出价的那个广告单元 ID,这样 CloudX 才能把实际成交价格归因到正确的广告单元。完整的字段说明与精度映射参见发布商上报收入数据。

用你已加载广告的广告单元 ID 创建出价:

// networkName 为可选参数;如果你知道获胜的广告来源,请传入它。
const adMobBid = CloudXArbiterBid.adMob({
    adUnitId: adMobAdUnitId,
    networkName: adMobNetworkName ?? 'admob',
});

// Ad Manager 广告单元 ID 的格式为 /NNNNNNN/placement/name。
const adManagerBid = CloudXArbiterBid.gam({
    adUnitId: '/21775744923/example/interstitial',
});

// 候选广告加载完成后运行,早于展示位。
nextWinner = await CloudX.arbiter({
    bids: [CloudXArbiterBid.cloudX(cloudXAdInfo), adMobBid, adManagerBid],
});

// 在展示位运行。这里没有网络请求。
const showAd = () => {
    if (nextWinner?.platform === 'ADMOB') {
        // 展示 AdMob 广告
    } else if (nextWinner?.platform === 'GAM') {
        // 展示 Ad Manager 广告
    }
    nextWinner = null;
};

请保存结果,而不要在仲裁器返回处直接展示——参见何时运行仲裁器。

获胜的 Google 出价会报告自己的平台——字符串 'ADMOB' 或 'GAM'。你不再需要像处理自定义出价那样,通过结果中的 platformName 来区分这两个来源。

传入空白的广告单元 ID,出价对象仍会正常构建,不会导致应用崩溃,但它不具备可用的身份信息:既不会被定价,也会被服务器拒绝。

使用 pre-bid ILRD 手动输入价格

部分 AdMob 账户可以在 pre-bid 阶段提供展示级收益数据(ILRD):已加载广告的广告价值在加载时即可获取,早于广告展示。这是一项受账户控制的历史能力,请与你的 Google 客户团队确认你的账户是否已启用。在展示之前就已知的精确单次展示价格,是自行提供价格优于 CloudX 估算值的唯一场景。

将 pre-bid 广告价值作为 manualRevenuePerImpressionUSD 传入,它会覆盖估算值:

const adMobBid = CloudXArbiterBid.adMob({
    adUnitId: adMobAdUnitId,
    manualRevenuePerImpressionUSD: preBidPricePerImpressionUSD,
});

你拿到的数值单位取决于底层的原生 SDK:Google Mobile Ads SDK 在 Android 上以微单位(micros)报告广告价值,在 iOS 上则以货币单位报告,因此请先换算为单次展示的美元金额再传入。

该数值的处理方式:

  • 0.0 是一个真实的价格。它表示该出价价值为零——而不是价格缺失。
  • 负值和非有限值不是有效价格,因此会被视为缺失并记录日志。
  • 空白的广告单元 ID 会导致手动价格被完全丢弃,因为没有身份信息的出价无法通过校验。

manualRevenuePerImpressionUSD 是单次展示的美元收益,而不是 CPM;非美元金额必须先换算为美元。AdMob 广告价值的各平台换算规则请参见 Android 和 iOS 页面。

支持的广告格式

arbiter 与广告格式无关:它接受任何已加载的 CloudX 广告,且相同的字段映射适用于任意格式。

  • 全屏——插屏、激励视频、应用开屏。 在展示位之前提前准备获胜方;参见何时运行仲裁器。
  • 视图——banner、MREC。 先仲裁,再渲染获胜方;视图附加规则参见 Banner 与 MREC。

自动刷新会与仲裁冲突:刷新可能会在 arbiter 已选出获胜方之后替换掉该广告。请在 CloudX 控制台中禁用自动刷新,并在 banner 或 MREC 广告上调用 stopAutoRefresh();同时也应禁用其他参与仲裁的广告网络的自动刷新。

CloudXBannerAd.stopAutoRefresh(adUnitId);
// CloudXMRECAd.stopAutoRefresh(adUnitId);

只通过 showAd(adUnitId) 展示获胜出价对应的广告;其余广告应通过 hideAd(adUnitId) 保持隐藏(或从不展示)。

推荐的刷新流程:获胜方展示后,仅从获胜的广告网络加载新的填充;保留已有填充的非获胜广告,只对未填充的网络重新发起请求;待响应返回后重新运行 arbiter。每 20-30 秒刷新一次展示中的广告——间隔过短会降低 CPM 表现。

自定义出价输入

当需要让 Trusted Arbiter 比较 CloudX 与没有专用出价工厂方法的第三方平台时,可以使用 CloudXArbiterBid.custom()。

const customBid = CloudXArbiterBid.custom({
    platformName: 'my_mediation_platform',
    networkName: 'winning_demand_source',
    revenuePerImpressionUSD: 0.00125,
    precision: 'EXACT',
    extras: { ad_unit: 'third-party-ad-unit-id' },
});

const result = await CloudX.arbiter({
    bids: [CloudXArbiterBid.cloudX(cloudXAdInfo), customBid],
});

console.log('Selected platform:', result.platform);

当自定义出价胜出时,result.platform 为 'CUSTOM',result.platformName 为出价中传入的 platformName。revenuePerImpressionUSD 应传入单次展示的美元收益,而不是 CPM。请使用 'EXACT'、'ESTIMATED'、'PUBLISHER_DEFINED' 或 'UNDEFINED' 作为 precision 描述该收益值的精度。React Native 出价对象要求传入 networkName;如果价格来源没有提供获胜广告网络名称,请传入 ''。如果自定义出价缺少非空的 platformName、收益或精度,该出价会被丢弃并记录警告,不会参与竞争。