Overview
Native ads are an ad format that allows you to freely customize the ad UI to match your app’s design. You can provide a natural user experience by individually placing elements such as ad creatives (images, videos), headline, body, and CTA buttons.Key Features
- Complete UI Customization: Freely configure ad layout to match your app’s design system
- Various Media Support: Image and video ad creative support
- Flexible Click Area Settings: Full click or individual element click handling
- Profile Information Display: Advertiser profile logo, name, and link support
Classes and Interfaces
AdropNativeAd
Main class for loading and managing native ads.String
required
Ad unit ID (issued from console)
String
ID for contextual targeting (optional)
AdropNativeAdListener
Listener to handle ad events
Boolean
default:"false"
Whether to use custom click handling
Key Properties
| Property | Type | Description |
|---|---|---|
headline | String | Ad headline |
body | String | Ad body text |
icon | String | Icon image URL |
cover | String | Cover image URL |
advertiser | String | Deprecated. Use profile.displayName instead. |
advertiserURL | String | Deprecated. Use profile.link instead. |
accountTag | JSONObject | Deprecated. No longer supported. |
creativeTag | JSONObject | Deprecated. No longer supported. |
callToAction | String | Call-to-action text (e.g., “Learn More”) |
profile | AdropNativeAdProfile | Advertiser profile information |
isLoaded | Boolean | Whether ad loading is complete |
isDestroyed | Boolean | Whether ad is destroyed |
AdropNativeAdView
Container view for displaying native ads.Boolean
default:"false"
Whether to enable entire area click. When set to
true, click events occur on the entire ad view area.Key Methods
| Method | Description |
|---|---|
setIconView(view, listener) | Set icon image view (ImageView type) |
setHeadLineView(view, listener) | Set headline text view (TextView type) |
setBodyView(view) | Set body text view (TextView type) |
setMediaView(view) | Set media container view (AdropMediaView type) |
setAdvertiserView(view, listener) | Set advertiser text view (TextView type) |
setCallToActionView(view) | Set CTA button or text view |
setProfileLogoView(view, listener) | Set profile logo image view (ImageView type) |
setProfileNameView(view, listener) | Set profile name text view (TextView type) |
setNativeAd(ad) | Bind ad data to view |
destroy() | Release resources |
AdropMediaView
Media container view for displaying ad images or videos.<io.adrop.ads.nativeAd.AdropMediaView
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="200dp" />
For video creatives,
AdropMediaView must be bound through AdropNativeAdView.setMediaView(...). Reading ad.asset and playing the URL with your own player (ExoPlayer, MediaPlayer, or any third-party library) bypasses the SDK’s video tracking pipeline, so VTR is not measured and onAdVideoStart / onAdVideoEnd never fire. See Video Tracking and VTR.AdropNativeAdListener
Interface for handling ad events.interface AdropNativeAdListener {
fun onAdReceived(ad: AdropNativeAd)
fun onAdClicked(ad: AdropNativeAd)
fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode)
fun onAdImpression(ad: AdropNativeAd) // Optional implementation
fun onAdVideoStart(ad: AdropNativeAd) // Optional implementation
fun onAdVideoEnd(ad: AdropNativeAd) // Optional implementation
}
AdropNativeAdProfile
Data class containing advertiser profile information.| Property | Type | Description |
|---|---|---|
displayLogo | String | Profile logo image URL |
displayName | String | Profile display name |
link | String | Profile link URL |
Implementation Guide
1. XML Layout Setup
Define the layout to display native ads. UseAdropNativeAdView as the root and place ad elements inside.
res/layout/native_ad_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<io.adrop.ads.nativeAd.AdropNativeAdView
android:id="@+id/native_ad_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<!-- Profile area -->
<ImageView
android:id="@+id/ad_profile_logo"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/ad_profile_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/ad_profile_logo"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/ad_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:text="Ad"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/ad_profile_logo"
app:layout_constraintTop_toBottomOf="@id/ad_profile_name" />
<!-- Media area -->
<io.adrop.ads.nativeAd.AdropMediaView
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="@id/ad_profile_logo" />
<!-- Ad content -->
<TextView
android:id="@+id/ad_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/ad_media" />
<TextView
android:id="@+id/ad_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/ad_headline" />
<!-- Bottom area -->
<TextView
android:id="@+id/ad_advertiser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ad_body" />
<Button
android:id="@+id/ad_call_to_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ad_body" />
</androidx.constraintlayout.widget.ConstraintLayout>
</io.adrop.ads.nativeAd.AdropNativeAdView>
2. Load and Display Ad (Kotlin)
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import io.adrop.ads.model.AdropErrorCode
import io.adrop.ads.nativeAd.AdropMediaView
import io.adrop.ads.nativeAd.AdropNativeAd
import io.adrop.ads.nativeAd.AdropNativeAdListener
import io.adrop.ads.nativeAd.AdropNativeAdView
class NativeAdActivity : AppCompatActivity() {
private lateinit var nativeAd: AdropNativeAd
private lateinit var nativeAdView: AdropNativeAdView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_native_ad)
nativeAdView = findViewById(R.id.native_ad_view)
loadNativeAd()
}
private fun loadNativeAd() {
// 1. Create native ad instance
nativeAd = AdropNativeAd(
context = this,
unitId = "YOUR_UNIT_ID" // Replace with actual unit ID
)
// 2. Set listener
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
Log.d("Adrop", "Ad received successfully")
populateNativeAdView(ad)
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "Ad failed to receive: $errorCode")
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "Ad clicked")
}
override fun onAdImpression(ad: AdropNativeAd) {
Log.d("Adrop", "Ad impression")
}
override fun onAdVideoStart(ad: AdropNativeAd) {
Log.d("Adrop", "Ad video started")
}
override fun onAdVideoEnd(ad: AdropNativeAd) {
Log.d("Adrop", "Ad video ended")
}
}
// 3. Load ad
nativeAd.load()
}
private fun populateNativeAdView(ad: AdropNativeAd) {
// Set profile
val profileLogoView = findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(this).load(ad.profile.displayLogo).into(profileLogoView)
nativeAdView.setProfileLogoView(profileLogoView)
val profileNameView = findViewById<TextView>(R.id.ad_profile_name)
profileNameView.text = ad.profile.displayName
nativeAdView.setProfileNameView(profileNameView)
// Set headline
val headlineView = findViewById<TextView>(R.id.ad_headline)
headlineView.text = ad.headline
nativeAdView.setHeadLineView(headlineView)
// Set media
val mediaView = findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(mediaView)
// Set body
val bodyView = findViewById<TextView>(R.id.ad_body)
bodyView.text = ad.body
nativeAdView.setBodyView(bodyView)
// Set advertiser
val advertiserView = findViewById<TextView>(R.id.ad_advertiser)
advertiserView.text = ad.profile.displayName
nativeAdView.setAdvertiserView(advertiserView)
// Set CTA button
val ctaView = findViewById<Button>(R.id.ad_call_to_action)
ctaView.text = ad.callToAction
nativeAdView.setCallToActionView(ctaView)
// Bind ad data
nativeAdView.setNativeAd(ad)
}
override fun onDestroy() {
// Release resources
nativeAdView.destroy()
nativeAd.destroy()
super.onDestroy()
}
}
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import io.adrop.ads.model.AdropErrorCode
import io.adrop.ads.nativeAd.AdropMediaView
import io.adrop.ads.nativeAd.AdropNativeAd
import io.adrop.ads.nativeAd.AdropNativeAdListener
import io.adrop.ads.nativeAd.AdropNativeAdView
class NativeAdFragment : Fragment() {
private var nativeAd: AdropNativeAd? = null
private var nativeAdView: AdropNativeAdView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_native_ad, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
nativeAdView = view.findViewById(R.id.native_ad_view)
loadNativeAd()
}
private fun loadNativeAd() {
nativeAd = AdropNativeAd(
context = requireContext(),
unitId = "YOUR_UNIT_ID"
)
nativeAd?.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
populateNativeAdView(ad)
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "Ad load failed: $errorCode")
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "Ad clicked")
}
}
nativeAd?.load()
}
private fun populateNativeAdView(ad: AdropNativeAd) {
val view = requireView()
// Profile logo
val profileLogo = view.findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(this).load(ad.profile.displayLogo).into(profileLogo)
nativeAdView?.setProfileLogoView(profileLogo)
// Profile name
val profileName = view.findViewById<TextView>(R.id.ad_profile_name)
profileName.text = ad.profile.displayName
nativeAdView?.setProfileNameView(profileName)
// Headline
val headline = view.findViewById<TextView>(R.id.ad_headline)
headline.text = ad.headline
nativeAdView?.setHeadLineView(headline)
// Media
val media = view.findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView?.setMediaView(media)
// Body
val body = view.findViewById<TextView>(R.id.ad_body)
body.text = ad.body
nativeAdView?.setBodyView(body)
// Advertiser
val advertiser = view.findViewById<TextView>(R.id.ad_advertiser)
advertiser.text = ad.profile.displayName
nativeAdView?.setAdvertiserView(advertiser)
// CTA
val cta = view.findViewById<Button>(R.id.ad_call_to_action)
cta.text = ad.callToAction
nativeAdView?.setCallToActionView(cta)
// Binding
nativeAdView?.setNativeAd(ad)
}
override fun onDestroyView() {
nativeAdView?.destroy()
nativeAd?.destroy()
super.onDestroyView()
}
}
3. Load and Display Ad (Java)
Java
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import io.adrop.ads.model.AdropErrorCode;
import io.adrop.ads.nativeAd.AdropMediaView;
import io.adrop.ads.nativeAd.AdropNativeAd;
import io.adrop.ads.nativeAd.AdropNativeAdListener;
import io.adrop.ads.nativeAd.AdropNativeAdView;
public class NativeAdActivity extends AppCompatActivity {
private AdropNativeAd nativeAd;
private AdropNativeAdView nativeAdView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_native_ad);
nativeAdView = findViewById(R.id.native_ad_view);
loadNativeAd();
}
private void loadNativeAd() {
// 1. Create native ad instance
nativeAd = new AdropNativeAd(
this,
"YOUR_UNIT_ID", // Replace with actual unit ID
null // contextId (optional)
);
// 2. Set listener
nativeAd.setListener(new AdropNativeAdListener() {
@Override
public void onAdReceived(AdropNativeAd ad) {
Log.d("Adrop", "Ad received successfully");
populateNativeAdView(ad);
}
@Override
public void onAdFailedToReceive(AdropNativeAd ad, AdropErrorCode errorCode) {
Log.e("Adrop", "Ad failed to receive: " + errorCode);
}
@Override
public void onAdClicked(AdropNativeAd ad) {
Log.d("Adrop", "Ad clicked");
}
@Override
public void onAdImpression(AdropNativeAd ad) {
Log.d("Adrop", "Ad impression");
}
@Override
public void onAdVideoStart(AdropNativeAd ad) {
Log.d("Adrop", "Ad video started");
}
@Override
public void onAdVideoEnd(AdropNativeAd ad) {
Log.d("Adrop", "Ad video ended");
}
});
// 3. Load ad
nativeAd.load();
}
private void populateNativeAdView(AdropNativeAd ad) {
// Set profile logo
ImageView profileLogoView = findViewById(R.id.ad_profile_logo);
Glide.with(this).load(ad.getProfile().getDisplayLogo()).into(profileLogoView);
nativeAdView.setProfileLogoView(profileLogoView, null);
// Set profile name
TextView profileNameView = findViewById(R.id.ad_profile_name);
profileNameView.setText(ad.getProfile().getDisplayName());
nativeAdView.setProfileNameView(profileNameView, null);
// Set headline
TextView headlineView = findViewById(R.id.ad_headline);
headlineView.setText(ad.getHeadline());
nativeAdView.setHeadLineView(headlineView, null);
// Set media
AdropMediaView mediaView = findViewById(R.id.ad_media);
nativeAdView.setMediaView(mediaView);
// Set body
TextView bodyView = findViewById(R.id.ad_body);
bodyView.setText(ad.getBody());
nativeAdView.setBodyView(bodyView);
// Set advertiser
TextView advertiserView = findViewById(R.id.ad_advertiser);
advertiserView.setText(ad.getProfile().getDisplayName());
nativeAdView.setAdvertiserView(advertiserView, null);
// Set CTA button
Button ctaView = findViewById(R.id.ad_call_to_action);
ctaView.setText(ad.getCallToAction());
nativeAdView.setCallToActionView(ctaView);
// Bind ad data
nativeAdView.setNativeAd(ad);
}
@Override
protected void onDestroy() {
// Release resources
if (nativeAdView != null) {
nativeAdView.destroy();
}
if (nativeAd != null) {
nativeAd.destroy();
}
super.onDestroy();
}
}
Advanced Features
Entire Click Area Setting
You can set the entire ad view area to trigger click events.nativeAdView.isEntireClick = true
nativeAdView.setEntireClick(true);
When
isEntireClick is set to true, the entire ad view becomes a clickable area. Individual element click listeners will not work.Individual Element Click Listeners
You can set custom click listeners on specific ad elements.// Custom action on advertiser click
nativeAdView.setAdvertiserView(advertiserView) {
Log.d("Adrop", "Advertiser clicked")
// Implement custom action
}
// Custom action on profile logo click
nativeAdView.setProfileLogoView(profileLogoView) {
Log.d("Adrop", "Profile logo clicked")
// Implement custom action
}
// Custom action on advertiser click
nativeAdView.setAdvertiserView(advertiserView, new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("Adrop", "Advertiser clicked");
// Implement custom action
}
});
// Custom action on profile logo click
nativeAdView.setProfileLogoView(profileLogoView, new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("Adrop", "Profile logo clicked");
// Implement custom action
}
});
Contextual Targeting
You can request ads matching a specific context.val nativeAd = AdropNativeAd(
context = this,
unitId = "YOUR_UNIT_ID",
contextId = "SPORTS_NEWS" // Context ID
)
AdropNativeAd nativeAd = new AdropNativeAd(
this,
"YOUR_UNIT_ID",
"SPORTS_NEWS" // Context ID
);
Using in RecyclerView
You can display native ads as items in a RecyclerView.RecyclerView Adapter
class ContentAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val VIEW_TYPE_CONTENT = 0
const val VIEW_TYPE_AD = 1
const val AD_INTERVAL = 5 // Show ad every 5 items
}
private val items = mutableListOf<Any>()
private val nativeAds = mutableMapOf<Int, AdropNativeAd>()
override fun getItemViewType(position: Int): Int {
return if (position % (AD_INTERVAL + 1) == AD_INTERVAL) {
VIEW_TYPE_AD
} else {
VIEW_TYPE_CONTENT
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_AD -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_native_ad, parent, false)
NativeAdViewHolder(view)
}
else -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_content, parent, false)
ContentViewHolder(view)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is NativeAdViewHolder -> loadNativeAd(holder, position)
is ContentViewHolder -> holder.bind(items[position])
}
}
private fun loadNativeAd(holder: NativeAdViewHolder, position: Int) {
// Reuse already loaded ad
nativeAds[position]?.let { ad ->
if (ad.isLoaded) {
holder.bind(ad)
return
}
}
// Load new ad
val nativeAd = AdropNativeAd(holder.itemView.context, "YOUR_UNIT_ID")
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
nativeAds[position] = ad
holder.bind(ad)
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "Ad load failed: $errorCode")
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "Ad clicked")
}
}
nativeAd.load()
}
class NativeAdViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val nativeAdView: AdropNativeAdView = itemView.findViewById(R.id.native_ad_view)
fun bind(ad: AdropNativeAd) {
val profileLogo = itemView.findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(itemView.context).load(ad.profile.displayLogo).into(profileLogo)
nativeAdView.setProfileLogoView(profileLogo)
val profileName = itemView.findViewById<TextView>(R.id.ad_profile_name)
profileName.text = ad.profile.displayName
nativeAdView.setProfileNameView(profileName)
val headline = itemView.findViewById<TextView>(R.id.ad_headline)
headline.text = ad.headline
nativeAdView.setHeadLineView(headline)
val media = itemView.findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(media)
val body = itemView.findViewById<TextView>(R.id.ad_body)
body.text = ad.body
nativeAdView.setBodyView(body)
val advertiser = itemView.findViewById<TextView>(R.id.ad_advertiser)
advertiser.text = ad.profile.displayName
nativeAdView.setAdvertiserView(advertiser)
val cta = itemView.findViewById<Button>(R.id.ad_call_to_action)
cta.text = ad.callToAction
nativeAdView.setCallToActionView(cta)
nativeAdView.setNativeAd(ad)
}
}
fun destroy() {
nativeAds.values.forEach { it.destroy() }
nativeAds.clear()
}
}
Test Unit IDs
Use the following test unit IDs during development and testing.| Format | Test Unit ID |
|---|---|
| Native (Image) | PUBLIC_TEST_UNIT_ID_NATIVE |
| Native Video (16:9) | PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_16_9 |
| Native Video (9:16) | PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_9_16 |
Be sure to replace with actual unit IDs before production deployment.
Lifecycle Management
Resource Release in Activity/Fragment
Native ads must be released when no longer in use to prevent memory leaks.override fun onDestroy() {
nativeAdView.destroy()
nativeAd.destroy()
super.onDestroy()
}
override fun onDestroyView() {
nativeAdView?.destroy()
nativeAd?.destroy()
super.onDestroyView()
}
@Override
protected void onDestroy() {
if (nativeAdView != null) {
nativeAdView.destroy();
}
if (nativeAd != null) {
nativeAd.destroy();
}
super.onDestroy();
}
Resource Release in RecyclerView
When using ads in a RecyclerView Adapter, all ads must be released when the Activity or Fragment is destroyed.override fun onDestroy() {
adapter.destroy()
super.onDestroy()
}
Video Tracking and VTR
Adrop measures video metrics for native creatives — including impression-to-completion (VTR),onAdVideoStart, and onAdVideoEnd — only when the video is rendered through the SDK’s media surface (AdropMediaView bound via AdropNativeAdView.setMediaView(...)).
The AdropNativeAd.asset property returns a URL pointing to the underlying image or video file. It is exposed as supplementary metadata (for example, for thumbnails or your own analytics); it is not intended to drive playback.
Do not feed the
asset URL into a custom video player (ExoPlayer, MediaPlayer, or any third-party player) for a video native ad. When the SDK’s media surface is bypassed:- VTR (Video Through Rate) is not collected for that placement.
onAdVideoStart/onAdVideoEndcallbacks never fire.- Aggregate video performance for the unit will under-report or report zero.
onAdClicked) and impression (onAdImpression) tracking still work because they are wired to the container view, but the video-specific signals are lost.Recommended pattern
Bind the SDK media surface for every video native:val mediaView = findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(mediaView) // Required for video tracking
nativeAdView.setNativeAd(ad)
AdropMediaView mediaView = findViewById(R.id.ad_media);
nativeAdView.setMediaView(mediaView); // Required for video tracking
nativeAdView.setNativeAd(ad);
If a placement absolutely requires a custom player and you accept that VTR will not be measured, you can still surface click and impression attribution by keeping the ad bound to
AdropNativeAdView. In that case, treat the placement as non-VTR inventory in your internal reporting and avoid mixing it with SDK-measured video performance.Best Practices
1. Display Only Required Elements
You don’t need to display all ad elements. Selectively use only the elements needed to match your app’s design.// Minimal configuration example (media + CTA only)
nativeAdView.setMediaView(mediaView)
nativeAdView.setCallToActionView(ctaView)
nativeAdView.setNativeAd(ad)
2. Use Image Loading Libraries
Use image loading libraries like Glide or Coil when displaying icon, cover, and profile images.// Glide example
Glide.with(context)
.load(ad.icon)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(iconView)
3. Reuse Ad Views
When displaying ads in a RecyclerView, cache and reuse ad instances.4. Appropriate Ad Placement
Place ads naturally without degrading user experience.- Content feed: 1 ad every 5-10 items
- Detail screen: Place at bottom of content
- Scroll view: Place to match natural content flow
5. Error Handling
Implement appropriate fallback handling for ad load failures.override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
when (errorCode) {
AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
// No ad available - hide ad area
nativeAdView.visibility = View.GONE
}
AdropErrorCode.ERROR_CODE_NETWORK -> {
// Network error - retry logic
Handler(Looper.getMainLooper()).postDelayed({
nativeAd.load()
}, 3000)
}
else -> {
Log.e("Adrop", "Ad load failed: $errorCode")
}
}
}
Complete Example
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import io.adrop.ads.model.AdropErrorCode
import io.adrop.ads.nativeAd.AdropMediaView
import io.adrop.ads.nativeAd.AdropNativeAd
import io.adrop.ads.nativeAd.AdropNativeAdListener
import io.adrop.ads.nativeAd.AdropNativeAdView
class NativeAdExampleActivity : AppCompatActivity() {
private lateinit var nativeAd: AdropNativeAd
private lateinit var nativeAdView: AdropNativeAdView
private lateinit var loadingView: View
private lateinit var errorView: View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_native_ad_example)
nativeAdView = findViewById(R.id.native_ad_view)
loadingView = findViewById(R.id.loading_view)
errorView = findViewById(R.id.error_view)
findViewById<Button>(R.id.retry_button).setOnClickListener {
loadNativeAd()
}
loadNativeAd()
}
private fun loadNativeAd() {
showLoading()
nativeAd = AdropNativeAd(
context = this,
unitId = "PUBLIC_TEST_UNIT_ID_NATIVE"
)
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
Log.d("Adrop", "Ad received: unitId=${ad.unitId}, txId=${ad.txId}")
populateNativeAdView(ad)
showAd()
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "Ad failed to receive: $errorCode")
showError()
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "Ad clicked: txId=${ad.txId}")
}
override fun onAdImpression(ad: AdropNativeAd) {
Log.d("Adrop", "Ad impression: txId=${ad.txId}")
}
}
nativeAd.load()
}
private fun populateNativeAdView(ad: AdropNativeAd) {
// Enable entire click area (optional)
// nativeAdView.isEntireClick = true
// Profile logo
val profileLogoView = findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(this)
.load(ad.profile.displayLogo)
.circleCrop()
.into(profileLogoView)
nativeAdView.setProfileLogoView(profileLogoView)
// Profile name
val profileNameView = findViewById<TextView>(R.id.ad_profile_name)
profileNameView.text = ad.profile.displayName
nativeAdView.setProfileNameView(profileNameView)
// Headline
val headlineView = findViewById<TextView>(R.id.ad_headline)
headlineView.text = ad.headline
nativeAdView.setHeadLineView(headlineView)
// Media (image or video)
val mediaView = findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(mediaView)
// Body
val bodyView = findViewById<TextView>(R.id.ad_body)
bodyView.text = ad.body
nativeAdView.setBodyView(bodyView)
// Advertiser
val advertiserView = findViewById<TextView>(R.id.ad_advertiser)
advertiserView.text = ad.profile.displayName
nativeAdView.setAdvertiserView(advertiserView) {
Log.d("Adrop", "Advertiser clicked: ${ad.profile.displayName}")
}
// CTA button
val ctaView = findViewById<Button>(R.id.ad_call_to_action)
ctaView.text = ad.callToAction
nativeAdView.setCallToActionView(ctaView)
// Bind ad data
nativeAdView.setNativeAd(ad)
}
private fun showLoading() {
loadingView.visibility = View.VISIBLE
nativeAdView.visibility = View.GONE
errorView.visibility = View.GONE
}
private fun showAd() {
loadingView.visibility = View.GONE
nativeAdView.visibility = View.VISIBLE
errorView.visibility = View.GONE
}
private fun showError() {
loadingView.visibility = View.GONE
nativeAdView.visibility = View.GONE
errorView.visibility = View.VISIBLE
}
override fun onDestroy() {
nativeAdView.destroy()
nativeAd.destroy()
super.onDestroy()
}
}
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import io.adrop.ads.model.AdropErrorCode;
import io.adrop.ads.nativeAd.AdropMediaView;
import io.adrop.ads.nativeAd.AdropNativeAd;
import io.adrop.ads.nativeAd.AdropNativeAdListener;
import io.adrop.ads.nativeAd.AdropNativeAdView;
public class NativeAdExampleActivity extends AppCompatActivity {
private AdropNativeAd nativeAd;
private AdropNativeAdView nativeAdView;
private View loadingView;
private View errorView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_native_ad_example);
nativeAdView = findViewById(R.id.native_ad_view);
loadingView = findViewById(R.id.loading_view);
errorView = findViewById(R.id.error_view);
findViewById(R.id.retry_button).setOnClickListener(v -> loadNativeAd());
loadNativeAd();
}
private void loadNativeAd() {
showLoading();
nativeAd = new AdropNativeAd(
this,
"PUBLIC_TEST_UNIT_ID_NATIVE",
null
);
nativeAd.setListener(new AdropNativeAdListener() {
@Override
public void onAdReceived(AdropNativeAd ad) {
Log.d("Adrop", "Ad received: unitId=" + ad.getUnitId() + ", txId=" + ad.getTxId());
populateNativeAdView(ad);
showAd();
}
@Override
public void onAdFailedToReceive(AdropNativeAd ad, AdropErrorCode errorCode) {
Log.e("Adrop", "Ad failed to receive: " + errorCode);
showError();
}
@Override
public void onAdClicked(AdropNativeAd ad) {
Log.d("Adrop", "Ad clicked: txId=" + ad.getTxId());
}
@Override
public void onAdImpression(AdropNativeAd ad) {
Log.d("Adrop", "Ad impression: txId=" + ad.getTxId());
}
});
nativeAd.load();
}
private void populateNativeAdView(AdropNativeAd ad) {
// Profile logo
ImageView profileLogoView = findViewById(R.id.ad_profile_logo);
Glide.with(this)
.load(ad.getProfile().getDisplayLogo())
.circleCrop()
.into(profileLogoView);
nativeAdView.setProfileLogoView(profileLogoView, null);
// Profile name
TextView profileNameView = findViewById(R.id.ad_profile_name);
profileNameView.setText(ad.getProfile().getDisplayName());
nativeAdView.setProfileNameView(profileNameView, null);
// Headline
TextView headlineView = findViewById(R.id.ad_headline);
headlineView.setText(ad.getHeadline());
nativeAdView.setHeadLineView(headlineView, null);
// Media
AdropMediaView mediaView = findViewById(R.id.ad_media);
nativeAdView.setMediaView(mediaView);
// Body
TextView bodyView = findViewById(R.id.ad_body);
bodyView.setText(ad.getBody());
nativeAdView.setBodyView(bodyView);
// Advertiser
TextView advertiserView = findViewById(R.id.ad_advertiser);
advertiserView.setText(ad.getProfile().getDisplayName());
nativeAdView.setAdvertiserView(advertiserView, v -> {
Log.d("Adrop", "Advertiser clicked: " + ad.getProfile().getDisplayName());
});
// CTA button
Button ctaView = findViewById(R.id.ad_call_to_action);
ctaView.setText(ad.getCallToAction());
nativeAdView.setCallToActionView(ctaView);
// Bind ad data
nativeAdView.setNativeAd(ad);
}
private void showLoading() {
loadingView.setVisibility(View.VISIBLE);
nativeAdView.setVisibility(View.GONE);
errorView.setVisibility(View.GONE);
}
private void showAd() {
loadingView.setVisibility(View.GONE);
nativeAdView.setVisibility(View.VISIBLE);
errorView.setVisibility(View.GONE);
}
private void showError() {
loadingView.setVisibility(View.GONE);
nativeAdView.setVisibility(View.GONE);
errorView.setVisibility(View.VISIBLE);
}
@Override
protected void onDestroy() {
if (nativeAdView != null) {
nativeAdView.destroy();
}
if (nativeAd != null) {
nativeAd.destroy();
}
super.onDestroy();
}
}
Batch Loading (loads)
UseAdropNativeAd.loads() to request multiple native ads in a single batched call. This is useful for prefetching a pool of ads to insert into feeds, carousels, or paginated lists.
Signature
AdropNativeAd.loads(
context: Context,
unitId: String,
contextId: String? = null,
listener: AdropNativeAdListener,
)
AdropNativeAd.loads(context, "YOUR_UNIT_ID", null, listener);
Usage
AdropNativeAd.loads(
context = this,
unitId = "YOUR_UNIT_ID",
contextId = null,
listener = object : AdropNativeAdListener {
// Batch success — each ad is fully rendered and ready to bind.
override fun onAdsReceived(ads: List<AdropNativeAd>) {
ads.forEach { ad ->
// Bind to an AdropNativeAdView or store for later lifecycle.
bindToView(ad)
}
}
// Batch failure
override fun onAdsFailedToReceive(errorCode: AdropErrorCode) {
Log.e("Adrop", "Batch load failed: $errorCode")
}
// Single-load callbacks are NOT invoked on this path.
override fun onAdReceived(ad: AdropNativeAd) { /* batch path */ }
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {}
// Click / impression / video callbacks still fire per instance.
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "Native clicked: ${ad.creativeId}")
}
override fun onAdImpression(ad: AdropNativeAd) {
Log.d("Adrop", "Native impression: ${ad.creativeId}")
}
},
)
AdropNativeAd.loads(this, "YOUR_UNIT_ID", null, new AdropNativeAdListener() {
@Override
public void onAdsReceived(@NotNull List<AdropNativeAd> ads) {
for (AdropNativeAd ad : ads) {
bindToView(ad);
}
}
@Override
public void onAdsFailedToReceive(@NotNull AdropErrorCode errorCode) {
Log.e("Adrop", "Batch load failed: " + errorCode);
}
@Override
public void onAdReceived(@NotNull AdropNativeAd ad) { }
@Override
public void onAdFailedToReceive(@NotNull AdropNativeAd ad, @NotNull AdropErrorCode errorCode) { }
@Override
public void onAdClicked(@NotNull AdropNativeAd ad) {
Log.d("Adrop", "Native clicked: " + ad.getCreativeId());
}
@Override
public void onAdImpression(@NotNull AdropNativeAd ad) {
Log.d("Adrop", "Native impression: " + ad.getCreativeId());
}
});
Listener Callbacks
| Callback | When it fires |
|---|---|
onAdsReceived(ads) | Batch delivered. Every ad is fully rendered — creative is ready to bind. |
onAdsFailedToReceive(errorCode) | Request rejected, network failure, or no fillable ads were returned (ERROR_CODE_AD_NO_FILL). |
onAdClicked / onAdImpression / onAdVideoStart / onAdVideoEnd | Fire on each returned ad just like a single load(). |
The singular
onAdReceived / onAdFailedToReceive callbacks are not invoked on the loads() path. Use onAdsReceived / onAdsFailedToReceive as the sole batch signals.Constraints
- Maximum 5 ads per call. If the server returns more, only the first 5 are delivered.
- Backfill is not applied. If no direct ads fill,
onAdsFailedToReceiveis called withERROR_CODE_AD_NO_FILLregardless of backfill configuration. - Do not call
destroy()insideonAdsReceived. Bind each ad to anAdropNativeAdViewviasetNativeAd(...)or store for later lifecycle, thendestroy()on the parentActivity.onDestroy()(or equivalent). - Unlike
AdropBanner.loads(), the native batch waits for every returned ad to finish rendering before firingonAdsReceived, so each instance is fully rendered on delivery.
Backfill Ads
When backfill ads are enabled, backfill ads are automatically loaded when direct ads are unavailable. Use theisBackfilled property to check if the ad is a backfill ad.
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
if (ad.isBackfilled) {
println("Backfill ad loaded")
} else {
println("Direct ad loaded")
}
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
when (errorCode) {
AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
println("No direct ad, requesting backfill...")
}
AdropErrorCode.ERROR_CODE_AD_BACKFILL_NO_FILL -> {
println("Backfill ad also not available")
}
else -> {
println("Ad load failed: $errorCode")
}
}
}
}
nativeAd.setListener(new AdropNativeAdListener() {
@Override
public void onAdReceived(AdropNativeAd ad) {
if (ad.isBackfilled()) {
System.out.println("Backfill ad loaded");
} else {
System.out.println("Direct ad loaded");
}
}
@Override
public void onAdFailedToReceive(AdropNativeAd ad, AdropErrorCode errorCode) {
if (errorCode == AdropErrorCode.ERROR_CODE_AD_NO_FILL) {
System.out.println("No direct ad, requesting backfill...");
} else if (errorCode == AdropErrorCode.ERROR_CODE_AD_BACKFILL_NO_FILL) {
System.out.println("Backfill ad also not available");
} else {
System.out.println("Ad load failed: " + errorCode);
}
}
});
AdChoices Position
For backfill native ads, you can choose which corner displays the AdChoices badge. SetpreferredAdChoicesPosition on the AdropNativeAd instance before calling load().
import io.adrop.ads.nativeAd.AdropAdChoicesPosition
import io.adrop.ads.nativeAd.AdropNativeAd
val nativeAd = AdropNativeAd(this, "YOUR_UNIT_ID")
nativeAd.preferredAdChoicesPosition = AdropAdChoicesPosition.BOTTOM_RIGHT
nativeAd.load()
import io.adrop.ads.nativeAd.AdropAdChoicesPosition;
import io.adrop.ads.nativeAd.AdropNativeAd;
AdropNativeAd nativeAd = new AdropNativeAd(this, "YOUR_UNIT_ID");
nativeAd.setPreferredAdChoicesPosition(AdropAdChoicesPosition.BOTTOM_RIGHT);
nativeAd.load();
| Value | Description |
|---|---|
AdropAdChoicesPosition.TOP_LEFT | Top-left corner |
AdropAdChoicesPosition.TOP_RIGHT | Top-right corner (default) |
AdropAdChoicesPosition.BOTTOM_LEFT | Bottom-left corner |
AdropAdChoicesPosition.BOTTOM_RIGHT | Bottom-right corner |
This setting is a preferred hint for the backfill network and applies only to backfill native ads — direct ads are not affected. The value must be set before
load() is called. The backfill network may override the position depending on its policy.To use backfill ads, add the
io.adrop:adrop-ads-backfill dependency. See Getting Started.Next Steps
Banner Ads
Implement banner ads
Interstitial Ads
Implement interstitial ads
Targeting Settings
User properties and context targeting
Reference
Classes, listeners, error codes