跳转到主要内容
Trusted Arbiter 会比较已加载的 CloudX 出价与受支持的第三方出价,并返回选中的平台。CloudX SDK 4.1.0 及更高版本支持 CloudX、Unity LevelPlay 和 PubMatic 出价输入。
请在 CloudX.initialize() 完成后再调用 CloudX.arbiter()。在仲裁服务可用之前,SDK 会在传入的出价中回退选择可比较美元出价最高的平台。

基础 API

从已加载的广告创建出价候选项,然后传给 CloudX.arbiter()
// cloudXAd 是 CloudX onAdLoaded 回调中的 CloudXAd 对象。
// levelPlayAdInfo 是 Unity LevelPlay 广告信息对象。
// pobBid 是 PubMatic/OpenWrap 出价对象。
val bids = listOf(
    CloudXArbiterBid.cloudX(cloudXAd),
    CloudXArbiterBid.levelPlay(
        networkName = levelPlayAdInfo.adNetwork,
        revenue = levelPlayAdInfo.revenue,
        precision = levelPlayAdInfo.precision,
    ),
    CloudXArbiterBid.pubmatic(
        price = pobBid.price,
        partnerName = pobBid.partnerName,
    )
)

val configuration = CloudXArbiterConfiguration.builder(bids).build()

CloudX.arbiter(configuration, object : CloudXArbiterListener {
    override fun onCompleted(result: CloudXArbiterResult) {
        Log.d("CloudX", "选中的平台: ${result.platform.name}")
    }
})
CloudXArbiterBid.cloudX() 接收 CloudX 加载回调中的 CloudXAd 对象。levelPlay() 接收 Unity LevelPlay 广告信息值。pubmatic() 接收 PubMatic OpenWrap 出价价格和可选的合作伙伴名称。出价工厂方法中的 partnerNameextras 映射均为可选参数。onCompleted() 在主线程上回调,因此你可以直接在其中展示广告或更新 UI。 result.platform 在选中平台时为 CloudXArbiterPlatform.CLOUDXLEVELPLAYPUBMATIC;当无法选出获胜平台时(例如未传入任何出价),则为 CloudXArbiterPlatform.NONE
Kotlin 调用方可以使用挂起函数重载来代替监听器,它会返回相同的 CloudXArbiterResult
val result = CloudX.arbiter(configuration)
Log.d("CloudX", "选中的平台: ${result.platform.name}")

分步示例:在 CloudX 与 LevelPlay 之间仲裁

本演练展示了应读取哪个 Unity LevelPlay 回调,以及应将哪些值传入 CloudX.arbiter()。示例使用插屏广告,但相同的字段映射适用于任何广告格式。
1

加载两个候选项

创建 CloudX 和 LevelPlay 插屏广告,绑定监听器,并为每个平台启动加载。
val cloudXInterstitial = CloudX.createInterstitial(context, "YOUR_CLOUDX_AD_UNIT_ID")
cloudXInterstitial.listener = cloudXListener
cloudXInterstitial.load()

val levelPlayInterstitial = LevelPlayInterstitialAd("YOUR_LEVELPLAY_AD_UNIT_ID")
levelPlayInterstitial.setListener(levelPlayListener)
levelPlayInterstitial.loadAd()
2

保存每个平台已加载的广告

LevelPlay 在其 onAdLoaded 回调中提供 LevelPlayAdInfo;CloudX 在 onAdLoaded 中提供 CloudXAd。请保存两者——下一步会从中读取仲裁输入。
private var cloudXAd: CloudXAd? = null
private var levelPlayInfo: LevelPlayAdInfo? = null

// CloudXInterstitialListener
override fun onAdLoaded(cloudXAd: CloudXAd) {
    this.cloudXAd = cloudXAd
}

// LevelPlayInterstitialAdListener
override fun onAdLoaded(levelPlayAdInfo: LevelPlayAdInfo) {
    levelPlayInfo = levelPlayAdInfo
}
3

将值映射为出价

LevelPlayAdInfo 读取 LevelPlay 字段,并传给 CloudXArbiterBid.levelPlay()。CloudX 出价直接接收 CloudXAdlistOfNotNull 只会提交实际加载成功的平台。
LevelPlayAdInfo 字段类型CloudXArbiterBid.levelPlay 参数
adNetworkStringnetworkName
revenueDoublerevenue
precisionStringprecision
val bids = listOfNotNull(
    cloudXAd?.let { CloudXArbiterBid.cloudX(it) },
    levelPlayInfo?.let { info ->
        CloudXArbiterBid.levelPlay(
            networkName = info.adNetwork,   // LevelPlayAdInfo.adNetwork
            revenue = info.revenue,         // LevelPlayAdInfo.revenue
            precision = info.precision,     // LevelPlayAdInfo.precision
        )
    },
)
4

运行仲裁器

将出价封装进 CloudXArbiterConfiguration,连同监听器传给 CloudX.arbiter()onCompleted() 在主线程上回调。
val configuration = CloudXArbiterConfiguration.builder(bids).build()
CloudX.arbiter(configuration, object : CloudXArbiterListener {
    override fun onCompleted(result: CloudXArbiterResult) {
        showWinner(result)
    }
})
5

展示获胜平台

根据 result.platform 进行分支,展示获胜平台的广告。CloudXArbiterPlatform.NONE 表示未选出获胜平台——此时不展示广告,继续应用流程。
private fun showWinner(result: CloudXArbiterResult) {
    when (result.platform) {
        CloudXArbiterPlatform.CLOUDX -> cloudXInterstitial.show(activity)
        CloudXArbiterPlatform.LEVELPLAY -> levelPlayInterstitial.showAd(activity)
        else -> { } // CloudXArbiterPlatform.NONE——无获胜平台;不展示广告
    }
}
下方的 ArbiterInterstitialController 将上述步骤封装为一个可复用的组件,会在到达广告位之前提前准备好获胜平台。

插屏广告示例

这个插屏广告示例会在两个平台之间仲裁:CloudX 和 Unity LevelPlay。在到达广告位之前先准备好获胜平台:
  1. 并行加载 CloudX 和 LevelPlay。
  2. 等待两个平台都加载完成或加载失败。
  3. 只将已加载的候选项提交给 Trusted Arbiter。
  4. 缓存选中的平台。
  5. 到达广告位时,立即展示缓存的获胜广告。
如果两个平台都加载失败,开始新的加载周期。如果到达广告位时还没有准备好获胜平台,则继续应用流程,不展示广告。
ArbiterInterstitialController.kt
/**
 * 提前准备好 Trusted Arbiter 的获胜平台,以便在到达广告位时立即展示插屏广告。
 *
 * 并行加载 [cloudXInterstitial] 和 [levelPlayInterstitial],等待两者都加载完成或加载失败后,
 * 将已加载的候选项提交给 [CloudX.arbiter],并将选中的 [CloudXArbiterPlatform] 缓存到
 * [nextWinner] 中。
 */
class ArbiterInterstitialController(
    private val cloudXInterstitial: CloudXInterstitialAd,
    private val levelPlayInterstitial: LevelPlayInterstitialAd,
) {
    /** 在获胜平台准备就绪后接收仲裁结果。 */
    interface Listener {
        /** 当仲裁器为下一次展示选定 [platform] 时调用。 */
        fun onWinnerPrepared(platform: CloudXArbiterPlatform)
    }

    /** 设置该属性以监听 [Listener.onWinnerPrepared] 回调。 */
    var listener: Listener? = null
    private var cloudXAd: CloudXAd? = null
    private var cloudXLoadDone = false
    private var levelPlayAdInfo: LevelPlayAdInfo? = null
    private var levelPlayLoadDone = false
    private var nextWinner: CloudXArbiterPlatform? = null

    init {
        cloudXInterstitial.listener = createCloudXListener()
        levelPlayInterstitial.setListener(createLevelPlayListener())
    }

    /** 为当前没有缓存广告的每个平台启动加载。 */
    fun loadMissingAds() {
        if (cloudXAd == null) cloudXInterstitial.load()
        if (levelPlayAdInfo == null) levelPlayInterstitial.loadAd()
    }

    /**
     * 在 [activity] 上展示已准备好的获胜广告,仅在实际调用展示方法时返回 true。
     *
     * 当没有就绪的获胜平台,或缓存的广告已不再可用时返回 false,此时会启动新的加载周期。
     */
    fun showAtPlacement(activity: Activity): Boolean {
        return when (nextWinner) {
            CloudXArbiterPlatform.CLOUDX -> showCloudX(activity)
            CloudXArbiterPlatform.LEVELPLAY -> showLevelPlay(activity)
            else -> false
        }
    }

    /**
     * 在两个平台都完成加载后运行仲裁器,然后缓存获胜平台。
     *
     * 在两个加载都完成之前会提前返回。如果两个平台都未加载成功,则重新启动加载周期;
     * 否则将已加载的候选项提交给 [CloudX.arbiter]。
     */
    private fun maybePrepareWinner() {
        if (!cloudXLoadDone || !levelPlayLoadDone) return

        if (cloudXAd == null && levelPlayAdInfo == null) {
            cloudXLoadDone = false
            levelPlayLoadDone = false
            loadMissingAds()
            return
        }

        val bids = listOfNotNull(
            cloudXAd?.let { CloudXArbiterBid.cloudX(it) },
            levelPlayAdInfo?.let { adInfo ->
                CloudXArbiterBid.levelPlay(
                    networkName = adInfo.adNetwork,
                    revenue = adInfo.revenue,
                    precision = adInfo.precision,
                )
            }
        )

        CloudX.arbiter(
            configuration = CloudXArbiterConfiguration.builder(bids).build(),
            listener = object : CloudXArbiterListener {
                override fun onCompleted(result: CloudXArbiterResult) {
                    nextWinner = result.platform
                    listener?.onWinnerPrepared(result.platform)
                }
            }
        )
    }

    private fun showCloudX(activity: Activity): Boolean {
        if (cloudXInterstitial.isAdReady) {
            cloudXInterstitial.show(activity)
            return true
        }

        clearCloudXAndLoadMissingAds()
        return false
    }

    private fun showLevelPlay(activity: Activity): Boolean {
        if (levelPlayInterstitial.isAdReady) {
            levelPlayInterstitial.showAd(activity)
            return true
        }

        clearLevelPlayAndLoadMissingAds()
        return false
    }

    private fun clearCloudXAndLoadMissingAds() {
        cloudXAd = null
        cloudXLoadDone = false
        nextWinner = null
        loadMissingAds()
    }

    private fun clearLevelPlayAndLoadMissingAds() {
        levelPlayAdInfo = null
        levelPlayLoadDone = false
        nextWinner = null
        loadMissingAds()
    }

    private fun createCloudXListener() = object : CloudXInterstitialListener {
        override fun onAdLoaded(cloudXAd: CloudXAd) {
            this@ArbiterInterstitialController.cloudXAd = cloudXAd
            cloudXLoadDone = true
            maybePrepareWinner()
        }

        override fun onAdLoadFailed(adUnitId: String, cloudXError: CloudXError) {
            cloudXAd = null
            cloudXLoadDone = true
            maybePrepareWinner()
        }

        override fun onAdDisplayed(cloudXAd: CloudXAd) = Unit

        override fun onAdDisplayFailed(cloudXAd: CloudXAd, cloudXError: CloudXError) {
            clearCloudXAndLoadMissingAds()
        }

        override fun onAdHidden(cloudXAd: CloudXAd) {
            clearCloudXAndLoadMissingAds()
        }

        override fun onAdClicked(cloudXAd: CloudXAd) = Unit
    }

    private fun createLevelPlayListener() = object : LevelPlayInterstitialAdListener {
        override fun onAdLoaded(levelPlayAdInfo: LevelPlayAdInfo) {
            this@ArbiterInterstitialController.levelPlayAdInfo = levelPlayAdInfo
            levelPlayLoadDone = true
            maybePrepareWinner()
        }

        override fun onAdLoadFailed(levelPlayAdError: LevelPlayAdError) {
            levelPlayAdInfo = null
            levelPlayLoadDone = true
            maybePrepareWinner()
        }

        override fun onAdInfoChanged(levelPlayAdInfo: LevelPlayAdInfo) {
            this@ArbiterInterstitialController.levelPlayAdInfo = levelPlayAdInfo
        }

        override fun onAdDisplayed(levelPlayAdInfo: LevelPlayAdInfo) = Unit

        override fun onAdDisplayFailed(
            levelPlayAdError: LevelPlayAdError,
            levelPlayAdInfo: LevelPlayAdInfo
        ) {
            clearLevelPlayAndLoadMissingAds()
        }

        override fun onAdClosed(levelPlayAdInfo: LevelPlayAdInfo) {
            clearLevelPlayAndLoadMissingAds()
        }

        override fun onAdClicked(levelPlayAdInfo: LevelPlayAdInfo) = Unit
    }
}
showAtPlacement() 只有在实际调用广告展示方法时才会返回 trueonAdInfoChanged() 会在 LevelPlay 广告仍然处于已加载状态时,保持缓存的 LevelPlay 候选项为最新。 对于 PubMatic OpenWrap,使用 CloudXArbiterBid.pubmatic(price, partnerName) 创建第三方出价。如果仲裁服务不可用,SDK 会在传入的受支持出价中回退选择可比较美元出价最高的平台。