Rewarded Ads

Integrate CloudX rewarded ads in Android apps

Rewarded ads give users a reward — typically in-app currency or bonus content — in exchange for watching a fullscreen ad.

CloudX-rendered creatives

CloudX-served creatives are rendered by the built-in renderer, which ships inside the core SDK — no separate renderer adapter module is required. The creative type is selected automatically from the bid response, so no publisher integration is needed to render either format. The renderer supports MRAID 3.0 (HTML) and VAST creatives across the ad formats:

Ad formatHTML (MRAID 3.0)VAST
Banner✓—
MREC✓✓
Interstitial✓✓
Rewarded✓✓
class MainActivity : AppCompatActivity(), CloudXRewardedListener, CloudXAdRevenueListener {
    private lateinit var rewardedAd: CloudXRewardedAd
    private var retryAttempt = 0

    private fun createRewardedAd() {
        rewardedAd = CloudX.createRewarded(this, "your-rewarded-ad-unit-id")
        rewardedAd.listener = this
        rewardedAd.revenueListener = this
        rewardedAd.load()
    }

    private fun showRewardedAd() {
        if (rewardedAd.isAdReady) {
            // Basic show
            rewardedAd.show(this)

            // Or with optional placement and custom data for tracking
            // rewardedAd.show(this, "bonus_coins", "level:5,coins:100")
        } else {
            Log.w("CloudX", "Rewarded ad not ready yet")
        }
    }

    // CloudXRewardedListener callbacks
    override fun onAdLoaded(cloudXAd: CloudXAd) {
        Log.d("CloudX", "Rewarded ad loaded from ${cloudXAd.networkName}")
        retryAttempt = 0
    }

    /**
     * Retries loading with exponential backoff: delay doubles with each attempt
     * (2s → 4s → 8s … 32s max) to avoid hammering the server during transient
     * failures. [retryAttempt] resets to 0 in [onAdLoaded].
     */
    override fun onAdLoadFailed(adUnitId: String, cloudXError: CloudXError) {
        Log.e("CloudX", "Rewarded ad failed to load: ${cloudXError.message}")
        retryAttempt++
        val delayMillis = TimeUnit.SECONDS.toMillis(2.0.pow(retryAttempt.coerceAtMost(5)).toLong())
        Handler(Looper.getMainLooper()).postDelayed({ rewardedAd.load() }, delayMillis)
    }

    override fun onAdDisplayed(cloudXAd: CloudXAd) {
        Log.d("CloudX", "Rewarded ad displayed")
    }

    override fun onAdDisplayFailed(cloudXAd: CloudXAd, cloudXError: CloudXError) {
        Log.e("CloudX", "Rewarded ad failed to display: ${cloudXError.message}")
        rewardedAd.load()
    }

    override fun onAdHidden(cloudXAd: CloudXAd) {
        Log.d("CloudX", "Rewarded ad hidden")
        // Reload for next use
        rewardedAd.load()
    }

    override fun onAdClicked(cloudXAd: CloudXAd) {
        Log.d("CloudX", "Rewarded ad clicked")
    }

    override fun onUserRewarded(cloudXAd: CloudXAd, reward: CloudXReward) {
        Log.d("CloudX", "User rewarded: ${reward.amount} ${reward.label}")
        // Grant the reward to the user
    }

    // CloudXAdRevenueListener callback
    override fun onAdRevenuePaid(cloudXAd: CloudXAd) {
        Log.d("CloudX", "Rewarded bid-time revenue: ${cloudXAd.revenue} from ${cloudXAd.networkName}")
    }

    override fun onDestroy() {
        super.onDestroy()
        rewardedAd.destroy()
    }
}

Expired ads

A loaded ad that is not shown before it expires is reloaded automatically on the same ad object. The current ad stays ready and can still be shown until the replacement is ready.

  • To be told when a replacement is ready, set an expirationListener. A replacement that loads on its own does not call onAdLoaded again.
  • If you show the ad while the replacement loads, the replacement is kept. It becomes ready after the ad closes, and the expirationListener is called then.
  • If you call load() while the replacement is loading, including a load() from onAdHidden, no second request starts. You receive onAdLoaded with the new ad, followed by onExpiredAdReloaded.
  • If the replacement fails to load while the expired ad is still loaded, onAdLoadFailed fires and the ad is no longer ready. Call load() again, after a delay rather than in a tight loop.
rewardedAd.expirationListener = CloudXAdExpirationListener { expiredAd, newAd ->
    Log.d("CloudX", "Expired rewarded ad replaced by an ad from ${newAd.networkName}")
}