Skip to content

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

ParameterTypeRequiredDescription
placementIdStringYesPlacement ID
adViewSizeAdViewSizeNoAd size, specifies width/height (unit: dp), only effective for template feed (FeedType.EXPRESS), default AdViewSize() (width=screen width dp, height=200)
scenarioKeyStringNoAd display scenario key, used for scenario statistics
userIdStringNoUser ID, for personalized strategy
customDataMap<String, String>NoCustom 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 by UjuAdObject.getNativeObject(activity, adConfig)
  • Variable type: NativeAdObject? changed to UjuAdObject?
  • ecpm retrieval: Simplified chained call, use nativeAd?.getAdInfo()?.ecpm to get directly

4. NativeAdData Creative Data Fields

UjuAdObject.getAdData() returns a NativeAdData object, containing the following fields (used for self-rendering feed data population):

FieldTypeDescription
titleStringAd title
descStringAd description
sourceStringAd source (e.g., advertiser name)
iconUrlStringAd icon URL
mediaTypeAdMediaTypeMedia type: SINGLE_IMAGE(1) single image / MULTI_IMAGE(2) multi-image / VIDEO(3) video
imageUrlStringSingle image URL (used in single-image mode)
imageUrlListList<String>Image URL list (used in multi-image mode)
callToActionStringCall-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:

FieldTypeRequiredDescription
titleIdIntYesTitle view ID
descIdIntYesDescription view ID
sourceIdIntYesSource view ID
iconIdIntYesIcon view ID
imageIdIntNoMain image view ID
imageViewsList<ImageView>NoImage view list (used in multi-image mode)
mediaViewIdIntNoMedia view ID (for video ads)
callToActionIdIntYesCall-to-action button view ID
logoLayoutIdIntNoAd logo layout ID
clickViewsIdsList<Int>NoClickable view ID list (extra click views beyond the default click area)
dislikeIdIntNoDislike button view ID

6. UjuAdInfo Ad Information Fields

UjuAdObject.getAdInfo() returns a UjuAdInfo object, containing ad metadata:

FieldTypeDescription
ecpmFloatAd bid (eCPM), unit: yuan
placementIdStringPlacement ID
soltIdStringAd code slot ID (note: source code spelling is soltId)
platformIdIntAd platform ID (see AdPlatformType enum)
adFormatIntAd 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) with UjuAdObject.getNativeObject(activity, adConfig)
  • Change variable type declaration from NativeAdObject? to UjuAdObject?
  • Simplify ecpm retrieval to nativeAd?.getAdInfo()?.ecpm
  • Optional: Add scenarioKey parameter in UjuAdConfig for scenario statistics