Teads Android SDK Integration Guide
This guide provides comprehensive instructions for integrating the Teads SDK into your Android application. Whether you're building a news app, content platform, or any other type of application, this guide will help you implement both video advertising and content recommendations.
Installation
For detailed installation instructions, see Installation.
SDK Initialization
Initialize the Teads SDK as early as possible in your app's lifecycle:
import tv.teads.sdk.TeadsSDK
// Initialize the SDK with your partner key
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        TeadsSDK.configure(applicationContext, "YOUR_PARTNER_KEY")
       // Enable test mode for development
       if (BuildConfig.DEBUG) {
          TeadsSDK.testMode = true
          TeadsSDK.testLocation = "us" // Optional: Test specific geolocations
       }
    }
}
Important: Calling the method the TeadsSDK.configure with your YOUR_PARTNER_KEY is mandatory when using the Feed or Recommendations placements. Otherwise, you can skip this part.
Implementation Samples
For complete working examples and sample applications, refer to our public GitHub repositories:
- Android Sample App - Complete Android implementation with all placement types
These repositories contain:
- Full working applications demonstrating all placement types
- Best practices and common integration patterns
Media Placement (Video Ads)
Media placements display premium video advertisements within your content.
import tv.teads.sdk.combinedsdk.adplacement.TeadsAdPlacementMedia
import tv.teads.sdk.combinedsdk.adplacement.config.TeadsAdPlacementMediaConfig
import tv.teads.sdk.combinedsdk.adplacement.interfaces.TeadsAdPlacementEventsDelegate
import tv.teads.sdk.combinedsdk.TeadsAdPlacementEventName
import android.net.Uri
class ContentActivity : AppCompatActivity(), TeadsAdPlacementEventsDelegate {
    private var mediaPlacement: TeadsAdPlacementMedia? = null
    private lateinit var binding: ActivityArticleBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityArticleBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupMediaPlacement()
    }
    private fun setupMediaPlacement() {
        // Create configuration
        val config = TeadsAdPlacementMediaConfig(
            pid = 84242,  // Your unique Placement ID
            articleUrl = Uri.parse("https://yoursite.com/article")
        )
        // Create placement with delegate
        mediaPlacement = TeadsAdPlacementMedia(
           this, // Context
           config, // Placement config
           this // Event delegate
        )
        // Load the ad
        val mediaAdView = mediaPlacement?.loadAd()
        
        // Add to your view hierarchy
       binding.myContainerAdView.addView(mediaAdView)
    }
   
    override fun onPlacementEvent(
       placement: TeadsAdPlacement<*, *>, 
       event: TeadsAdPlacementEventName, 
       data: Map<String, Any>?
    ) {
       // Listen the ad lifecycle events
    }
   override fun onDestroy() {
      super.onDestroy()
      teadsAdPlacementMedia?.clean()
   }
}
Important: When using Media Placement, it's mandatory to set your ad container to dynamic sizing instead of fixed dimensions.
This is because ad creatives served for Media Placement have not a fixed size. The VPAID standard allows them to have dynamic dimensions and aspect ratios, meaning they can change size and shape on the fly.
By using dynamic sizing, you ensure the ad container can automatically resize to perfectly fit the creative's content, preventing issues like cropping or blank spaces. This dynamic resizing is crucial for delivering a great user experience and accurately measuring ad performance throughout the entire ad lifecycle, not just at initialization.
XML Implementation: Use android:layout_height="wrap_content" for your ad container.
Compose Implementation: Use Modifier.wrapContentHeight() or Modifier.height(IntrinsicSize.Min) for your ad container.
Feed Placement (Content Recommendations)
Feed placements display content recommendation widgets, perfect for keeping users engaged with related content.
import tv.teads.sdk.combinedsdk.adplacement.TeadsAdPlacementFeed
import tv.teads.sdk.combinedsdk.adplacement.config.TeadsAdPlacementFeedConfig
import tv.teads.sdk.combinedsdk.adplacement.interfaces.TeadsAdPlacementEventsDelegate
import tv.teads.sdk.combinedsdk.TeadsAdPlacementEventName
import android.net.Uri
class ContentActivity : AppCompatActivity(), TeadsAdPlacementEventsDelegate {
    private var feedPlacement: TeadsAdPlacementFeed? = null
    private lateinit var binding: ActivityContentBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityContentBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupFeedPlacement()
    }
    private fun setupFeedPlacement() {
        // Create configuration
        val config = TeadsAdPlacementFeedConfig(
            articleUrl = Uri.parse("https://yoursite.com/article"),
            widgetId = "MB_1", // Your unique Placement ID
            installationKey = "YOUR_INSTALLATION_KEY",
            widgetIndex = 0,
            userId = null,  // Optional user ID for personalization
            darkMode = false,
            testDisplay = false,
            extId = null,  // External ID
            extSecondaryId = null,  // External Secondary ID
            obPubImpl = null   // OB Publisher Implementation
        )
        // Create placement
        feedPlacement = TeadsAdPlacementFeed(
           this, // Context
           config, // Placement config
           this // Event delegate
        )
        // Load the feed
        val feedView = feedPlacement?.loadAd()
        // Add to your view hierarchy
        binding.myContainerAdView.addView(feedView)
    }
   override fun onConfigurationChanged(newConfig: Configuration) {
      super.onConfigurationChanged(newConfig)
      feedPlacement?.onActivityConfigurationChanged()
   }
    override fun onPlacementEvent(
       placement: TeadsAdPlacement<*, *>,
       event: TeadsAdPlacementEventName,
       data: Map<String, Any>?
    ) {
        // Listen the ad lifecycle events
       if (placement is TeadsAdPlacementFeed && event == TeadsAdPlacementEventName.CLICKED_ORGANIC) {
          val url = data?.get("url") as? String
          url?.let { 
              // Programmatically open browser with the url 
          }
       }
    }
}
Explore More Feature
The Feed placement supports an "Explore More" feature that shows additional content when users navigate away:
// Enable explore more when the user leaves the article
override fun onBackPressed() {
   super.onBackPressed()
   TeadsAdPlacementFeed.handleExploreMore(MyContentActivity@this) {
      runOnUiThread { finish() }
   }
}
Media Native Placement
Media Native placements allow you to create custom layouts for video ads that match your app's design.
import tv.teads.sdk.combinedsdk.adplacement.TeadsAdPlacementMediaNative
import tv.teads.sdk.combinedsdk.adplacement.config.TeadsAdPlacementMediaNativeConfig
import tv.teads.sdk.combinedsdk.adplacement.interfaces.TeadsAdPlacementEventsDelegate
import tv.teads.sdk.combinedsdk.TeadsAdPlacementEventName
import tv.teads.sdk.renderer.NativeAdView
import android.net.Uri
class ContentActivity : AppCompatActivity(), TeadsAdPlacementEventsDelegate {
    private var nativePlacement: TeadsAdPlacementMediaNative? = null
    private var nativeAdView: NativeAdView? = null
    private lateinit var binding: ActivityNativeAdBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityNativeAdBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupNativeAd()
    }
    private fun setupNativeAd() {
        // Create custom native ad view
        nativeAdView = createCustomNativeAdView()
        // Create configuration
        val config = TeadsAdPlacementMediaNativeConfig(
            124859, // Your unique Placement ID
            Uri.parse("https://yoursite.com/article")
        )
        // Create placement
        nativePlacement = TeadsAdPlacementMediaNative(
           this, // Context 
           config, // Placement config
           this // Event delegate
        )
        // Load and bind the ad
        nativePlacement
            ?.loadAd()
            ?.let { binder ->
               // Bind the ad native object to your native ad view
               binder(binding.myContainerAdView.nativeAdView)
           }
    }
    override fun onPlacementEvent(
       placement: TeadsAdPlacement, 
       event: TeadsAdPlacementEventName, 
       data: Map<String, Any>?
    ) {
       // Listen the ad lifecycle events
    }
   override fun onDestroy() {
      super.onDestroy()
      nativePlacement?.clean()
   }
}
Recommendations API
For programmatic access to content recommendations with custom UI:
import tv.teads.sdk.combinedsdk.adplacement.TeadsAdPlacementRecommendations
import tv.teads.sdk.combinedsdk.adplacement.config.TeadsAdPlacementRecommendationsConfig
import tv.teads.sdk.combinedsdk.adplacement.interfaces.TeadsAdPlacementEventsDelegate
import tv.teads.sdk.combinedsdk.TeadsAdPlacementEventName
import com.outbrain.OBSDK.Entities.OBRecommendation
import com.outbrain.OBSDK.Entities.OBRecommendationsResponse
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.TextView
class ContentActivity : AppCompatActivity(), TeadsAdPlacementEventsDelegate {
   private var recommendationsPlacement: TeadsAdPlacementRecommendations? = null
   private lateinit var binding: ActivityRecommendationsBinding
   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      binding = ActivityRecommendationsBinding.inflate(layoutInflater)
      setContentView(binding.root)
      loadRecommendations()
   }
   private fun loadRecommendations() {
      // Create configuration
      val config = TeadsAdPlacementRecommendationsConfig(
         Uri.parse("https://yoursite.com/article"),
         "SDK_1" // Your unique Placement ID
      )
      // Create placement
      recommendationsPlacement = TeadsAdPlacementRecommendations(
         config, // Placement config
         this // Event delegate
      )
      // Fetch recommendations
      lifecycleScope.launch {
         try {
            val recommendations = recommendationsPlacement?.loadAdSuspend()
            recommendations?.let { displayRecommendations(it) }
         } catch (e: Exception) {
            Log.e("TeadsSDK", "Failed to load recommendations: ${e.message}")
         }
      }
   }
   private fun displayRecommendations(recommendations: OBRecommendationsResponse) {
      binding.recommendationsContainer.removeAllViews()
      if (recommendations.all.isNotEmpty()) {
         recommendations.all.forEach { recommendations ->
            val recommendationView = createRecommendationView(recommendations)
            binding.recommendationsContainer.addView(recommendationView)
         }
      }
   }
   private fun createRecommendationView(recommendation: OBRecommendation): View {
      val binding = RecommendationItemBinding.inflate(LayoutInflater.from(this))
      binding.recommendationTitle.text = recommendation.content
      binding.recommendationSource.text = recommendation.sourceName
      // Load image (using your preferred image loading library)
      recommendation.thumbnail?.url?.let { imageUrl ->
         // E.g. Glide.with(this).load(imageUrl).into(binding.recommendationImage)
      }
      // Handle click
      binding.root.setOnClickListener {
         TeadsAdPlacementRecommendations.getUrl(recommendation)?.let { url ->
            // Open URL in browser
         }
      }
      return binding.root
   }
   override fun onPlacementEvent(
      placement: TeadsAdPlacement<*, *>,
      event: TeadsAdPlacementEventName,
      data: Map<String, Any>?
   ) {
      // Listen the ad lifecycle events
   }
}
Event Handling
Comprehensive Event Handling
class ComprehensiveEventHandler : TeadsAdPlacementEventsDelegate {
   override fun onPlacementEvent(
      placement: TeadsAdPlacement,
      event: TeadsAdPlacementEventName,
      data: Map<String, Any>?
   ) {
      Log.d("TeadsSDK", "Event: $event from $placement with data $data")
      when (event) {
         TeadsAdPlacementEventName.LOADED -> {
            // Placement is requested to load the ad
            Log.d("TeadsSDK", "Content fully loaded")
         }
          
         TeadsAdPlacementEventName.READY -> {
            // Ad is loaded and ready to display
            Log.d("TeadsSDK", "Ad is ready to be displayed")
         }
         TeadsAdPlacementEventName.RENDERED -> {
            // Ad has been rendered on screen
            Log.d("TeadsSDK", "Ad has been rendered")
         }
         TeadsAdPlacementEventName.VIEWED -> {
            // Ad has met IAB viewability standards
            Log.d("TeadsSDK", "Ad has met viewability criteria")
         }
         TeadsAdPlacementEventName.CLICKED -> {
            // User clicked on the ad
            val url = data?.get("url") as? String
            Log.d("TeadsSDK", "User clicked on ad: $url")
         }
         TeadsAdPlacementEventName.CLICKED_ORGANIC -> {
            // User clicked on organic content (Feed placement)
            val url = data?.get("url") as? String
            Log.d("TeadsSDK", "User clicked on organic content: $url")
            url?.let {
               // Open in browser or in-app browser
               val intent = Intent(Intent.ACTION_VIEW, Uri.parse(it))
               startActivity(intent)
            }
         }
         TeadsAdPlacementEventName.FAILED -> {
            // Ad failed to load
            // Show fallback content or hide ad container
            val reason = data?.get("reason") as? String
            Log.e("TeadsSDK", "Ad failed to load: $reason")
         }
         TeadsAdPlacementEventName.PLAY -> {
            // Video started playing
            Log.d("TeadsSDK", "Video started playing")
         }
         TeadsAdPlacementEventName.PAUSE -> {
            // Video paused
            Log.d("TeadsSDK", "Video paused")
         }
         TeadsAdPlacementEventName.COMPLETE -> {
            // Video completed
            Log.d("TeadsSDK", "Video completed")
         }
         TeadsAdPlacementEventName.START_PLAY_AUDIO -> {
            // Ad started playing audio
            Log.d("TeadsSDK", "Ad started playing audio")
         }
         TeadsAdPlacementEventName.STOP_PLAY_AUDIO -> {
            // Ad stopped playing audio
            Log.d("TeadsSDK", "Ad stopped playing audio")
         }
         TeadsAdPlacementEventName.HEIGHT_UPDATED -> {
            // Ad height changed (useful for dynamic layouts)
            // This is the height your ad container must match
            val height = data?.get("height") as? Int
            Log.d("TeadsSDK", "Ad height updated to: $height")
         }
         else -> {
            Log.d("TeadsSDK", "Other event: $event")
         }
      }
   }
}
Best Practices
1. Placement Lifecycle Management
Media and Media Native placements
Call clean() method for placements Media and Media Native when they're no longer needed to prevent memory leaks:
class ContentActivity : AppCompatActivity(), TeadsAdPlacementEventsDelegate {
   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      // ... setup your placements
   }
   override fun onDestroy() {
      super.onDestroy()
      when (placement) {
         is TeadsAdPlacementMedia -> placement.clean()
         is TeadsAdPlacementMediaNative -> placement.clean()
      }
   }
}
Feed and Recommendations placements
For Feed and Recommendation placements, ensure you load only one ad instance per screen.
To prevent redundant ad requests during view recycling (RecyclerView) or recomposition (LazyList), cache the loaded ad by holding a strong lifecycle aware reference to it.
2. Error Handling
Always implement proper error handling:
// Handle errors through the delegate
override fun onPlacementEvent(
   placement: TeadsAdPlacement,
   event: TeadsAdPlacementEventName,
   data: Map<String, Any>?
) {
   if (event == TeadsAdPlacementEventName.FAILED) {
      // Ad failed to load
      // Show fallback content or hide ad container
      val reason = data?.get("reason") as? String
      Log.e("TeadsSDK", "Ad failed to load: $reason")
   }
}
3. Testing
Use test mode during development:
// Enable test mode for development
if (BuildConfig.DEBUG) {
   TeadsSDK.testMode = true
   TeadsSDK.testLocation = "us" // Optional: Test specific geolocations
}
Troubleshooting
Common Issues and Solutions
- 
Ad not displaying - Verify your placement ID is correct
- Check network connectivity
- Ensure SDK is properly initialized
- Check delegate for error events
 
- 
Layout issues - Let the SDK manage ad view height
- Do not add invisible overlays on your ad container
 
- 
Memory leaks - Release resources and listeners in lifecycle methods (onDestroy).
- Avoid context leaks from inner classes; use WeakReferencefor long-lived operations.
- Cancel coroutines or background tasks when the ViewModelor UI component is cleared.
 
- Release resources and listeners in lifecycle methods (
- 
Performance issues - Preload ads when appropriate
- Limit number of concurrent placements
- Use lazy loading for off-screen content
 
Next Steps
- Review the Migration Guide for Teads Users
- Review the Migration Guide for Outbrain Users
- Learn about Privacy & Compliance
For additional support, see Support