The Metica Ads SDK for Android enables developers to integrate advertising into Android applications with support for interstitial, rewarded, banner, and MREC ad formats.
Requirements
Android API 21 (Android 5.0) or higher
Kotlin 1.8+
AppLovinSDK dependency (version 13.0.0 or higher, managed by Metica SDK)
Do not call, reference, or initialize the AppLovin SDK directly in your code. The Metica SDK manages all AppLovin integration internally. Direct usage may cause version conflicts and unexpected behavior.
Download
The Metica Android SDK is distributed via Maven Central. You can find the latest version and version history at:
Make sure you have Maven Central in your repositories:
Callback Interfaces
The SDK uses callback interfaces to communicate events back to your application.
MeticaInitCallback
Triggered when SDK initialization completes.
MeticaAdsLoadCallback
Handles ad loading events.
MeticaAdsShowCallback
Manages display lifecycle events for fullscreen ads.
MeticaAdsAdViewCallback
Handles banner and MREC ad view events. Extends MeticaAdsLoadCallback.
Privacy Configuration
Set privacy controls before initialization for GDPR and CCPA compliance:
Supported Ad Formats
Format
Enum
Dimensions
Interstitial
INTER
Full-screen
Rewarded
REWARD
Full-screen video
Banner
BANNER
320x50 (adaptive)
MREC
MREC
300x250
SDK Initialization
Initialize the SDK once during application startup, before loading any ads.
Initialization Response
The MeticaInitResponse contains a smartFloors object with two properties:
userGroup – The experiment group assigned to the current user: either TRIAL or HOLDOUT. This determines which cohort the user belongs to for A/B testing purposes.
isSuccess – A boolean indicating whether initialization completed successfully. If this is false, the user is automatically assigned to the holdout group.
These values are provided solely for analytics and logging. You do not need to implement any fallback logic—the SDK handles group assignment internally regardless of initialization outcome.
Using Kotlin Lambda
Interstitial Ads
Full-screen ads that cover the app interface.
Rewarded Ads
Full-screen video ads that reward users upon completion.
Banner Ads
Small rectangular ads (320x50) that remain on screen.
Banner Auto-Refresh Control
MREC Ads
Medium Rectangle ads (300x250).
Revenue Tracking
Track ad revenue for analytics and attribution.
MeticaAd Properties
Property
Type
Description
adUnitId
String
Unique identifier for the ad unit
revenue
Double?
Revenue in USD
networkName
String?
Ad network that served the ad
placementTag
String?
Placement identifier (if set)
adFormat
String
Format type (INTER, REWARD, BANNER, MREC)
creativeId
String?
Creative identifier
latency
Long?
Load latency in milliseconds
AppLovin Consent Utilities
Access AppLovin-specific consent information via MeticaSdk.Ads.Max:
Debug Logging
Enable logging for development and debugging:
Or via ADB:
Look for "Metica" tag in Logcat.
Best Practices
1
Initialize Early
Call MeticaSdk.initialize() as early as possible, ideally in Application.onCreate() or MainActivity.onCreate().
2
Set Privacy Before Init
Always configure privacy settings before calling initialize:
3
Preload Ads
Load the next ad immediately after showing:
4
Check Readiness Before Showing
5
Pass Activity Context
Always pass the current Activity when showing fullscreen ads:
6
Clean Up Banner/MREC Views
7
Handle Lifecycle for Ad Views
8
Implement Retry with Backoff
Use exponential backoff for failed loads to avoid overwhelming the network:
Thread Safety
All callbacks are delivered on the same thread that made the original call. If you call from the main thread, callbacks arrive on the main thread - safe for UI updates:
fun interface MeticaInitCallback {
fun onInit(initResponse: MeticaInitResponse)
}
interface MeticaAdsLoadCallback {
fun onAdLoadSuccess(meticaAd: MeticaAd)
fun onAdLoadFailed(meticaAdError: MeticaAdError)
}
interface MeticaAdsShowCallback {
fun onAdShowSuccess(meticaAd: MeticaAd)
fun onAdShowFailed(meticaAd: MeticaAd, meticaAdError: MeticaAdError)
fun onAdHidden(meticaAd: MeticaAd)
fun onAdClicked(meticaAd: MeticaAd)
fun onAdRevenuePaid(meticaAd: MeticaAd)
fun onAdRewarded(meticaAd: MeticaAd) // Only called for rewarded ads
}
interface MeticaAdsAdViewCallback : MeticaAdsLoadCallback {
fun onAdClicked(meticaAd: MeticaAd)
fun onAdRevenuePaid(meticaAd: MeticaAd)
}
Privacy settings
import com.metica.ads.MeticaAds
// GDPR - User consent for personalized ads
MeticaAds.PrivacySettings.setHasUserConsent(true, context)
// CCPA - Do Not Sell user data
MeticaAds.PrivacySettings.setDoNotSell(false, context)
Initialize Metica SDK
import android.app.Activity
import android.os.Bundle
import com.metica.MeticaInitCallback
import com.metica.MeticaInitConfig
import com.metica.MeticaInitResponse
import com.metica.MeticaSdk
import com.metica.ads.MeticaAds
import com.metica.ads.MeticaMediationInfo
import com.metica.ads.MeticaMediationInfo.MeticaMediationType
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initializeMeticaSdk()
}
private fun initializeMeticaSdk() {
// Set privacy settings BEFORE initialization
MeticaAds.PrivacySettings.setHasUserConsent(true, this)
MeticaAds.PrivacySettings.setDoNotSell(false, this)
val initConfig = MeticaInitConfig(
apiKey = "YOUR_API_KEY",
appId = "YOUR_APP_ID",
userId = "unique-user-id" // Optional - SDK generates one if empty
)
val mediationInfo = MeticaMediationInfo(
mediationType = MeticaMediationType.MAX,
key = "YOUR_APPLOVIN_SDK_KEY"
)
MeticaSdk.initialize(
context = this,
initConfig = initConfig,
mediationInfo = mediationInfo,
callback = object : MeticaInitCallback {
override fun onInit(initResponse: MeticaInitResponse) {
println("Current user is part of ${initResponse.smartFloors?.userGroup}")
println("Metica initialization completed: ${initResponse.smartFloors?.isSuccess}")
// SDK ready - start loading ads
loadAds()
}
}
)
}
private fun loadAds() {
// Load your ads here
}
}
MeticaSdk.initialize(
context = this,
initConfig = initConfig,
mediationInfo = mediationInfo
) { initResponse ->
println("Current user is part of ${initResponse.smartFloors?.userGroup}")
println("Metica initialization completed: ${initResponse.smartFloors?.isSuccess}")
}
InterstitialAdManager.kt
import android.app.Activity
import com.metica.MeticaSdk
import com.metica.ads.MeticaAd
import com.metica.ads.MeticaAdError
import com.metica.ads.MeticaAdsLoadCallback
import com.metica.ads.MeticaAdsShowCallback
class InterstitialAdManager(private val activity: Activity) :
MeticaAdsLoadCallback, MeticaAdsShowCallback {
private val adUnitId = "YOUR_INTERSTITIAL_AD_UNIT_ID"
private var retryAttempt = 0
private val maxRetryDelay = 64.0
fun loadAd() {
MeticaSdk.Ads.loadInterstitial(adUnitId, this)
}
fun showAd() {
if (MeticaSdk.Ads.isInterstitialReady(adUnitId)) {
MeticaSdk.Ads.showInterstitial(
activity = activity,
adUnitId = adUnitId,
placementId = "main_menu", // Optional - for analytics
customData = "level_complete", // Optional - for analytics
callback = this
)
} else {
println("Interstitial not ready")
}
}
// Load callbacks
override fun onAdLoadSuccess(meticaAd: MeticaAd) {
retryAttempt = 0
println("Interstitial loaded: ${meticaAd.adUnitId}")
}
override fun onAdLoadFailed(meticaAdError: MeticaAdError) {
println("Interstitial load failed: ${meticaAdError.message}")
// Exponential backoff retry
retryAttempt++
val delaySeconds = minOf(Math.pow(2.0, retryAttempt.toDouble()), maxRetryDelay)
activity.window.decorView.postDelayed({
println("Retrying interstitial load in ${delaySeconds}s")
loadAd()
}, (delaySeconds * 1000).toLong())
}
// Show callbacks
override fun onAdShowSuccess(meticaAd: MeticaAd) {
println("Interstitial shown")
}
override fun onAdShowFailed(meticaAd: MeticaAd, meticaAdError: MeticaAdError) {
println("Interstitial show failed: ${meticaAdError.message}")
}
override fun onAdHidden(meticaAd: MeticaAd) {
println("Interstitial hidden")
// Preload next ad
loadAd()
}
override fun onAdClicked(meticaAd: MeticaAd) {
println("Interstitial clicked")
}
override fun onAdRevenuePaid(meticaAd: MeticaAd) {
println("Interstitial revenue: ${meticaAd.revenue} from ${meticaAd.networkName}")
}
override fun onAdRewarded(meticaAd: MeticaAd) {
// Not called for interstitials
}
}
RewardedAdManager.kt
import android.app.Activity
import com.metica.MeticaSdk
import com.metica.ads.MeticaAd
import com.metica.ads.MeticaAdError
import com.metica.ads.MeticaAdsLoadCallback
import com.metica.ads.MeticaAdsShowCallback
class RewardedAdManager(private val activity: Activity) :
MeticaAdsLoadCallback, MeticaAdsShowCallback {
private val adUnitId = "YOUR_REWARDED_AD_UNIT_ID"
private var retryAttempt = 0
private val maxRetryDelay = 64.0
fun loadAd() {
MeticaSdk.Ads.loadRewarded(adUnitId, this)
}
fun showAd() {
if (MeticaSdk.Ads.isRewardedReady(adUnitId)) {
MeticaSdk.Ads.showRewarded(
activity = activity,
adUnitId = adUnitId,
placementId = "shop", // Optional - for analytics
customData = "extra_coins", // Optional - for analytics
callback = this
)
} else {
println("Rewarded ad not ready")
}
}
// Load callbacks
override fun onAdLoadSuccess(meticaAd: MeticaAd) {
retryAttempt = 0
println("Rewarded ad loaded")
}
override fun onAdLoadFailed(meticaAdError: MeticaAdError) {
println("Rewarded ad load failed: ${meticaAdError.message}")
// Exponential backoff retry
retryAttempt++
val delaySeconds = minOf(Math.pow(2.0, retryAttempt.toDouble()), maxRetryDelay)
activity.window.decorView.postDelayed({
println("Retrying rewarded load in ${delaySeconds}s")
loadAd()
}, (delaySeconds * 1000).toLong())
}
// Show callbacks
override fun onAdShowSuccess(meticaAd: MeticaAd) {
println("Rewarded ad shown")
}
override fun onAdShowFailed(meticaAd: MeticaAd, meticaAdError: MeticaAdError) {
println("Rewarded ad show failed: ${meticaAdError.message}")
}
override fun onAdHidden(meticaAd: MeticaAd) {
println("Rewarded ad hidden")
// Preload next ad
loadAd()
}
override fun onAdClicked(meticaAd: MeticaAd) {
println("Rewarded ad clicked")
}
override fun onAdRevenuePaid(meticaAd: MeticaAd) {
println("Rewarded ad revenue: ${meticaAd.revenue} from ${meticaAd.networkName}")
}
override fun onAdRewarded(meticaAd: MeticaAd) {
println("User earned reward!")
grantRewardToUser()
}
private fun grantRewardToUser() {
// Grant in-game currency, extra lives, etc.
println("Granting reward to user!")
}
}
BannerActivity.kt
import android.app.Activity
import android.os.Bundle
import android.view.Gravity
import android.widget.FrameLayout
import com.metica.MeticaSdk
import com.metica.ads.MeticaAd
import com.metica.ads.MeticaAdError
import com.metica.ads.MeticaAdView
import com.metica.ads.MeticaAdsAdViewCallback
class BannerActivity : Activity(), MeticaAdsAdViewCallback {
private val adUnitId = "YOUR_BANNER_AD_UNIT_ID"
private var bannerAdView: MeticaAdView? = null
private lateinit var bannerContainer: FrameLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Assuming you have a FrameLayout in your layout with id "banner_container"
bannerContainer = findViewById(R.id.banner_container)
loadBanner()
}
private fun loadBanner() {
// Clean up existing banner
bannerAdView?.destroy()
bannerAdView = null
bannerContainer.removeAllViews()
// Create banner ad view
bannerAdView = MeticaSdk.Ads.createBannerAdView(adUnitId).apply {
setListener(this@BannerActivity)
setPlacement("home_screen") // Optional - for analytics
}
// Add to container with layout params
val layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
gravity = Gravity.CENTER
}
bannerContainer.addView(bannerAdView, layoutParams)
// Load the ad
bannerAdView?.load()
}
override fun onAdLoadSuccess(meticaAd: MeticaAd) {
println("Banner loaded: ${meticaAd.adUnitId}")
}
override fun onAdLoadFailed(meticaAdError: MeticaAdError) {
println("Banner load failed: ${meticaAdError.message}")
}
override fun onAdClicked(meticaAd: MeticaAd) {
println("Banner clicked")
}
override fun onAdRevenuePaid(meticaAd: MeticaAd) {
println("Banner revenue: ${meticaAd.revenue}")
}
override fun onPause() {
super.onPause()
bannerAdView?.stopAutoRefresh()
}
override fun onResume() {
super.onResume()
bannerAdView?.startAutoRefresh()
}
override fun onDestroy() {
super.onDestroy()
bannerAdView?.destroy()
}
}
// Stop auto-refresh (e.g., when navigating away or during gameplay)
bannerAdView?.stopAutoRefresh()
// Resume auto-refresh
bannerAdView?.startAutoRefresh()
// Manual refresh (after stopping auto-refresh)
bannerAdView?.load()
MrecActivity.kt
import android.app.Activity
import android.os.Bundle
import android.view.Gravity
import android.widget.FrameLayout
import com.metica.MeticaSdk
import com.metica.ads.MeticaAd
import com.metica.ads.MeticaAdError
import com.metica.ads.MeticaAdView
import com.metica.ads.MeticaAdsAdViewCallback
class MrecActivity : Activity(), MeticaAdsAdViewCallback {
private val adUnitId = "YOUR_MREC_AD_UNIT_ID"
private var mrecAdView: MeticaAdView? = null
private lateinit var mrecContainer: FrameLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mrecContainer = findViewById(R.id.mrec_container)
loadMrec()
}
private fun loadMrec() {
// Clean up existing MREC
mrecAdView?.destroy()
mrecAdView = null
mrecContainer.removeAllViews()
// Create MREC ad view
mrecAdView = MeticaSdk.Ads.createMrecAdView(adUnitId).apply {
setListener(this@MrecActivity)
setPlacement("game_over_screen") // Optional - for analytics
}
// Add to container - MREC is 300x250
val layoutParams = FrameLayout.LayoutParams(
dpToPx(300),
dpToPx(250)
).apply {
gravity = Gravity.CENTER
}
mrecContainer.addView(mrecAdView, layoutParams)
// Load the ad
mrecAdView?.load()
}
private fun dpToPx(dp: Int): Int {
return (dp * resources.displayMetrics.density).toInt()
}
override fun onAdLoadSuccess(meticaAd: MeticaAd) {
println("MREC loaded: ${meticaAd.adUnitId}")
}
override fun onAdLoadFailed(meticaAdError: MeticaAdError) {
println("MREC load failed: ${meticaAdError.message}")
}
override fun onAdClicked(meticaAd: MeticaAd) {
println("MREC clicked")
}
override fun onAdRevenuePaid(meticaAd: MeticaAd) {
println("MREC revenue: ${meticaAd.revenue}")
}
override fun onPause() {
super.onPause()
mrecAdView?.stopAutoRefresh()
}
override fun onResume() {
super.onResume()
mrecAdView?.startAutoRefresh()
}
override fun onDestroy() {
super.onDestroy()
mrecAdView?.destroy()
}
}
onAdRevenuePaid example
override fun onAdRevenuePaid(meticaAd: MeticaAd) {
// Revenue in USD
val revenue = meticaAd.revenue ?: 0.0
val networkName = meticaAd.networkName ?: "unknown"
val adUnitId = meticaAd.adUnitId
val adFormat = meticaAd.adFormat
println("Ad revenue: $${"%.4f".format(revenue)}")
println("Network: $networkName")
println("Ad Unit: $adUnitId")
println("Format: $adFormat")
// Send to your analytics
trackRevenue(revenue, networkName, adUnitId)
}
// Check if user has provided consent
val hasConsent = MeticaSdk.Ads.Max.hasUserConsent()
// Check if consent status has been set
val isConsentSet = MeticaSdk.Ads.Max.isUserConsentSet()
// Get user's geographical category for consent purposes
val geography = MeticaSdk.Ads.Max.getConsentFlowUserGeography()
Enable Metica logs
import com.metica.internal.util.MeticaLogger
// Enable before initialization
MeticaLogger.enableLogs = true