Android Native Ad Integration
Overview
Native ads are a customizable ad format that blends seamlessly with the app UI, with the following characteristics:
- Highly customizable appearance
- Natural integration with app content
- Better user experience
- Higher click-through and conversion rates
Integration Steps
Refer to the NativeAdHelper in the demo.
1. Initialize the Native Ad
Initialize a native ad in an Activity or Fragment:
kotlin
/**
* Native Ad Helper
*
* Responsible for native ad loading, display, and lifecycle management
* Supports both template feed and self-rendering feed display modes
*
* @param activity Context Activity
* @param logger Log printing tool
*/
class NativeAdHelper(private val activity: Activity, private val logger: PrintLogger) {
/**
* Native ad object
*
* Used to manage native ad loading, display, and destruction
* Created via UjuAdObject factory method, uniformly manages all ad object types
*/
private var nativeAd: UjuAdObject? = null
/**
* Load a native ad
*
* This method creates an ad config, obtains the native ad object via the factory method,
* sets the ad listener, and finally calls load() to start loading the ad
*
* @param pId Placement ID
*/
fun load(pId: String) {
// Create the ad config object, set the placement ID and ad size
// scenarioKey is an optional parameter for tracking ad display scenarios
val adConfig = UjuAdConfig(
placementId = pId, // Native ad placement ID
adViewSize = AdViewSize(width = 600, height = 200) // Ad size: 600x200, only effective for template feed
)
// Obtain the native ad object via the UjuAdObject factory method
nativeAd = UjuAdObject.getNativeObject(activity, adConfig)
// Set the ad listener to monitor various ad events
nativeAd?.setAdObjectListener(object : FeedAdObjectListener {
/**
* Ad load success callback
*
* @param placementId Placement ID
*/
override fun onLoadSuccess(placementId: String) {
// Ad loaded successfully, can now display; recommend checking isReady before showing
logger.add("Native: onLoadSuccess")
}
/**
* Ad load failure callback
*
* @param error Error information
* @param placementId Placement ID
*/
override fun onLoadError(
error: UjuException,
placementId: String
) {
// Log the load error
logger.add("Native: onLoadError")
DemoLogUtils.d("Native: onLoadError")
}
/**
* Ad display error callback
*
* @param error Error information
*/
override fun onAdError(
error: UjuException
) {
// Log the ad error, including message and code
logger.add("Native: onAdError:message:${error.message}, code:${error.code}")
DemoLogUtils.e("Native: onAdError:message:${error.message}, code:${error.code}")
}
/**
* Ad display success callback
*/
override fun onAdShow() {
// Get ad info via getAdInfo(), read the ecpm value directly
val ecpm = nativeAd?.getAdInfo()?.ecpm
logger.add("Native: onAdShow: ecpm:$ecpm")
DemoLogUtils.d("Native: onAdShow: ecpm:$ecpm")
}
/**
* Ad clicked callback
*/
override fun onAdClicked() {
// Log the ad click event
logger.add("Native: onAdClicked")
}
/**
* Ad closed callback
*/
override fun onAdClosed() {
// Log the ad close event
logger.add("Native: onAdClosed")
}
/**
* Ad landing page closed callback
*/
override fun onLpClosed() {
// Log the landing page close event
logger.add("Native: onLpClosed")
}
})
// Start loading the ad
nativeAd?.load()
// Log the load ad event
logger.add("Native: load, placementId:${adConfig.placementId}")
DemoLogUtils.d("Native: load, placementId:${adConfig.placementId}")
}
/**
* Display the native ad
*
* Uses different display methods depending on the ad type (template feed or self-rendering feed)
* Template feed: directly call show()
* Self-rendering feed: custom layout, fill data and bind interactions
*
* @param viewGroup ViewGroup to hold the ad
*/
fun show(viewGroup: ViewGroup) {
// Check if the ad object has been created
val adObject = nativeAd ?: run {
logger.add("Native: please load the ad first")
return
}
// Check if the ad is ready
if (adObject.isReady()) {
// Determine the ad type: template feed or self-rendering feed
if (adObject.getFeedType() == FeedType.EXPRESS) {
// Template feed: directly call show() to display
adObject.show(activity, viewGroup)
} else {
// Self-rendering feed: custom layout, fill data and bind interactions
// Get the creative data
val data = adObject.getAdData()
if (data == null) {
// Creative data is empty, notify the user
Toast.makeText(activity, "Missing creative data", Toast.LENGTH_SHORT).show()
return
}
// Load the custom ad layout
val adView = activity.layoutInflater.inflate(R.layout.banner_feed_ad_view_layout, null)
// Fill ad data
adView.findViewById<TextView>(R.id.tvADTitle).text = data.title // Ad title
adView.findViewById<TextView>(R.id.tvADDesc).text = data.desc // Ad description
adView.findViewById<TextView>(R.id.tvADSource).text = data.source // Ad source
adView.findViewById<TextView>(R.id.btnADCreative).text = data.callToAction // Call-to-action button
// Get image views
val ivAdPic = adView.findViewById<ImageView>(R.id.ivADPic) // Ad main image
val ivAdSmall = adView.findViewById<ImageView>(R.id.ivADSmall) // Advertiser icon
// Determine the ad image URL
var imageUrl = ""
if (data.imageUrl != null) {
imageUrl = data.imageUrl.toString() // Use the single image URL
} else if (!data.imageUrlList.isNullOrEmpty()) {
imageUrl = data.imageUrlList?.get(0) ?: "" // Use the first image in the list
}
// Load the ad image
Glide.with(activity).load(imageUrl).into(ivAdPic)
// Load the advertiser icon
Glide.with(activity).load(data.iconUrl).into(ivAdSmall)
// Create the ad view binder, used to bind ad data and views
val binder = NativeAdViewBinder(
titleId = R.id.tvADTitle, // Title view ID
descId = R.id.tvADDesc, // Description view ID
sourceId = R.id.tvADSource, // Source view ID
imageId = R.id.ivADPic, // Image view ID
imageViews = listOf(ivAdPic), // Image view list
mediaViewId = R.id.flVideo, // Media view ID (for video ads)
iconId = R.id.ivADSmall, // Icon view ID
callToActionId = R.id.btnADCreative, // Call-to-action button ID
logoLayoutId = R.id.flADLogo // Ad logo layout ID
)
// Bind click responses so the ad can be clicked
adObject.registerViewForInteraction(
activity, // Context Activity
adView, // Ad view
viewGroup, // Container view
binder // View binder
)
}
} else {
// Ad not ready, log it
logger.add("Native: ad not ready")
}
}
/**
* Check if the ad is loaded
*
* @return true means the ad object has been created, false means not created
* Note: This method only checks whether the ad object exists, not whether it is ready
* To check if the ad can be displayed, use the isReady() method
*/
fun isLoaded(): Boolean {
return nativeAd != null
}
/**
* Destroy the ad object
*
* Call this method when the ad is no longer needed to destroy the ad object and release resources
*/
fun destroy() {
// Destroy the ad object
nativeAd?.destroy()
// Null out the ad object reference
nativeAd = null
}
}2. UjuAdConfig Configuration
| Parameter | Type | Required | Description |
|---|---|---|---|
placementId | String | Yes | Placement ID |
adViewSize | AdViewSize | No | Ad size, specifies width/height (unit: dp), only effective for template feed (FeedType.EXPRESS), default AdViewSize() (width=screen width dp, height=200) |
scenarioKey | String | No | Ad display scenario key, used for scenario statistics |
userId | String | No | User ID, for personalized strategy |
customData | Map<String, String> | No | Custom data, passed through to the ad server |
3. API Changes (v2.0)
The new SDK uses the UjuAdObject factory method pattern to uniformly manage all ad object types.
- Ad object creation:
NativeAdObject(activity, adConfig)is deprecated, replaced byUjuAdObject.getNativeObject(activity, adConfig) - Variable type:
NativeAdObject?changed toUjuAdObject? - ecpm retrieval: Simplified chained call, use
nativeAd?.getAdInfo()?.ecpmto get directly
4. NativeAdData Creative Data Fields
UjuAdObject.getAdData() returns a NativeAdData object, containing the following fields (used for self-rendering feed data population):
| Field | Type | Description |
|---|---|---|
title | String | Ad title |
desc | String | Ad description |
source | String | Ad source (e.g., advertiser name) |
iconUrl | String | Ad icon URL |
mediaType | AdMediaType | Media type: SINGLE_IMAGE(1) single image / MULTI_IMAGE(2) multi-image / VIDEO(3) video |
imageUrl | String | Single image URL (used in single-image mode) |
imageUrlList | List<String> | Image URL list (used in multi-image mode) |
callToAction | String | Call-to-action text (e.g., "Download Now") |
5. NativeAdViewBinder View Binder Fields
NativeAdViewBinder is used to bind ad views and data for self-rendering feed. Complete fields:
| Field | Type | Required | Description |
|---|---|---|---|
titleId | Int | Yes | Title view ID |
descId | Int | Yes | Description view ID |
sourceId | Int | Yes | Source view ID |
iconId | Int | Yes | Icon view ID |
imageId | Int | No | Main image view ID |
imageViews | List<ImageView> | No | Image view list (used in multi-image mode) |
mediaViewId | Int | No | Media view ID (for video ads) |
callToActionId | Int | Yes | Call-to-action button view ID |
logoLayoutId | Int | No | Ad logo layout ID |
clickViewsIds | List<Int> | No | Clickable view ID list (extra click views beyond the default click area) |
dislikeId | Int | No | Dislike button view ID |
6. UjuAdInfo Ad Information Fields
UjuAdObject.getAdInfo() returns a UjuAdInfo object, containing ad metadata:
| Field | Type | Description |
|---|---|---|
ecpm | Float | Ad bid (eCPM), unit: yuan |
placementId | String | Placement ID |
soltId | String | Ad code slot ID (note: source code spelling is soltId) |
platformId | Int | Ad platform ID (see AdPlatformType enum) |
adFormat | Int | Ad format |
Best Practices
1. Ad Layout Design
- Consistent with app style: Use the same design language as the app
- Clear ad label: Ensure users can identify it as an ad
- Reasonable information hierarchy: Title, description, and brand info clearly visible
- Responsive design: Adapt to different screen sizes
2. Optimization Tips
- Preload ads: Load ads before they need to be displayed
- Caching mechanism: Implement ad caching to improve display speed
- Alternate layouts: Create multiple layouts for different ads
- Performance optimization: Avoid over-rendering and memory leaks
3. Pitfalls to Avoid
- Do not hide the ad label
- Do not modify ad content
- Do not interfere with normal ad display
- Do not add irrelevant content to ads
FAQ
Q: Why is the native ad not showing?
A: Possible reasons:
- Incorrect ad unit ID
- Network connection issue
- Insufficient ad inventory
- Layout issue (container not visible or size is 0)
- Rendering code issues
Q: How to improve native ad effectiveness?
A: Recommendations:
- Carefully design the ad layout
- Ensure ads are relevant to app content
- Optimize ad loading and rendering
- Test different ad positions
- Analyze ad performance data
Q: Can native ads be used in RecyclerView?
A: Yes, implementation:
- Manage native ads in the ViewHolder
- Create a dedicated layout for ad items
- Properly handle ad loading and rendering
- Avoid frequently creating and destroying ads
Q: How to migrate from the old API to the new one?
A: Main changes:
- Replace
NativeAdObject(activity, adConfig)withUjuAdObject.getNativeObject(activity, adConfig) - Change variable type declaration from
NativeAdObject?toUjuAdObject? - Simplify ecpm retrieval to
nativeAd?.getAdInfo()?.ecpm - Optional: Add
scenarioKeyparameter inUjuAdConfigfor scenario statistics
