Skip to content

Android 原生广告集成

概述

原生广告是一种可以自定义样式的广告形式,能够与应用界面无缝融合,具有以下特点:

  • 高度自定义的外观
  • 与应用内容自然融合
  • 更好的用户体验
  • 较高的点击率和转化率

集成步骤

参考demo示例的NativeAdHelper

1. 初始化原生广告

在 Activity 或 Fragment 中初始化原生广告:

kotlin
/**
 * 原生广告帮助类
 * 
 * 负责原生广告的加载、展示和生命周期管理
 * 支持模板信息流和自渲染信息流两种展示方式
 * 
 * @param activity 上下文Activity
 * @param logger 日志打印工具
 */
class NativeAdHelper(private val activity: Activity, private val logger: PrintLogger) {

    /**
     * 原生广告对象
     * 
     * 用于管理原生广告的加载、展示和销毁
     */
    private var nativeAdObject: NativeAdObject? = null

    /**
     * 加载原生广告
     * 
     * 该方法会创建广告配置,初始化原生广告对象,并设置广告监听器
     * 最后调用load()方法开始加载广告
     */
    fun load() {
        // 创建广告配置对象,设置广告位ID和广告尺寸
        val adConfig = UjuAdConfig(
            placementId = DemoConfig.getNativePlacementId(), // 原生广告位ID
            adViewSize = AdViewSize(width = 320, height = 100) // 广告尺寸:320x100
        )
        
        // 初始化原生广告对象
        nativeAdObject = NativeAdObject(activity, adConfig)
        
        // 设置广告监听器,监听广告的各种事件
        nativeAdObject?.setAdObjectListener(object : FeedAdObjectListener {
            /**
             * 广告加载成功回调
             * 
             * @param placementId 广告位ID
             */
            override fun onLoadSuccess(placementId: String) {
                // 广告加载成功,此时可以展示,建议展示前判断isReady
                logger.add("Native: onLoadSuccess")
            }

            /**
             * 广告加载失败回调
             * 
             * @param error 错误信息
             * @param placementId 广告位ID
             */
            override fun onLoadError(
                error: UjuException,
                placementId: String
            ) {
                // 记录加载错误信息
                logger.add("Native: onLoadError")
                DemoLogUtils.d("Native: onLoadError")
            }

            /**
             * 广告展示过程中出错回调
             * 
             * @param error 错误信息
             * @param placementId 广告位ID
             */
            override fun onAdError(
                error: UjuException,
                placementId: String
            ) {
                // 记录广告错误信息,包含错误消息和错误码
                logger.add("Native: onAdError:message:${error.message}, code:${error.code}")
                DemoLogUtils.e("Native: onAdError:message:${error.message}, code:${error.code}")
            }

            /**
             * 广告展示成功回调
             */
            override fun onAdShow() {
                // 获取广告信息,记录ecpm值
                val adInfo = nativeAdObject?.getAdInfo()
                logger.add("Native: onAdShow: ecpm:${adInfo?.ecpm}")
                DemoLogUtils.d("Native: onAdShow: ecpm:${adInfo?.ecpm}")
            }

            /**
             * 广告被点击回调
             */
            override fun onAdClicked() {
                // 记录广告点击事件
                logger.add("Native: onAdClicked")
            }

            /**
             * 广告关闭回调
             */
            override fun onAdClosed() {
                // 记录广告关闭事件
                logger.add("Native: onAdClosed")
            }

            /**
             * 广告落地页关闭回调
             */
            override fun onLpClosed() {
                // 记录落地页关闭事件
                logger.add("Native: onLpClosed")
            }

        })
        
        // 开始加载广告
        nativeAdObject?.load()
        // 记录加载广告的事件
        logger.add("Native: load, placementId:${adConfig.placementId}")
        DemoLogUtils.d("Native: load, placementId:${adConfig.placementId}")
    }

    /**
     * 展示原生广告
     * 
     * 根据广告类型(模板信息流或自渲染信息流)采用不同的展示方式
     * 模板信息流:直接调用show()方法展示
     * 自渲染信息流:自定义布局,填充数据并绑定交互
     * 
     * @param viewGroup 用于容纳广告的ViewGroup
     */
    fun show(viewGroup: ViewGroup) {
        // 检查广告对象是否已创建
        val adObject = nativeAdObject ?: run {
            logger.add("Native: 请先加载广告")
            return
        }

        // 检查广告是否已准备就绪
        if (adObject.isReady()) {
            // 判断广告类型:模板信息流或自渲染信息流
            if (adObject.getFeedType() == FeedType.EXPRESS) {
                // 模板信息流:直接调用show()方法展示
                adObject.show(activity, viewGroup)
            } else {
                // 自渲染信息流:自定义布局,填充数据并绑定交互
                
                // 获取物料数据
                val data = adObject.getAdData()
                if (data == null) {
                    // 物料数据为空,提示用户
                    Toast.makeText(activity, "缺失物料数据", Toast.LENGTH_SHORT).show()
                    return
                }

                // 加载自定义广告布局
                val adView = activity.layoutInflater.inflate(R.layout.banner_feed_ad_view_layout, null)
                
                // 填充广告数据
                adView.findViewById<TextView>(R.id.tvADTitle).text = data.title // 广告标题
                adView.findViewById<TextView>(R.id.tvADDesc).text = data.desc // 广告描述
                adView.findViewById<TextView>(R.id.tvADSource).text = data.source // 广告来源
                adView.findViewById<TextView>(R.id.btnADCreative).text = data.callToAction // 行动号召按钮

                // 获取图片视图
                val ivAdPic = adView.findViewById<ImageView>(R.id.ivADPic) // 广告大图
                val ivAdSmall = adView.findViewById<ImageView>(R.id.ivADSmall) // 广告商图标

                // 确定广告图片URL
                var imageUrl = ""
                if (data.imageUrl != null) {
                    imageUrl = data.imageUrl.toString() // 使用单张图片URL
                } else if (!data.imageUrlList.isNullOrEmpty()) {
                    imageUrl = data.imageUrlList?.get(0) ?: "" // 使用图片列表中的第一张
                }

                // 加载广告图片
                Glide.with(activity).load(imageUrl).into(ivAdPic)

                // 加载广告商图标
                Glide.with(activity).load(data.iconUrl).into(ivAdSmall)

                // 创建广告视图绑定器,用于绑定广告数据和视图
                val binder = NativeAdViewBinder(
                    titleId = R.id.tvADTitle, // 标题视图ID
                    descId = R.id.tvADDesc, // 描述视图ID
                    sourceId = R.id.tvADSource, // 来源视图ID
                    imageId = R.id.ivADPic, // 图片视图ID
                    imageViews = listOf(ivAdPic), // 图片视图列表
                    mediaViewId = R.id.flVideo, // 媒体视图ID(用于视频广告)
                    iconId = R.id.ivADSmall, // 图标视图ID
                    callToActionId = R.id.btnADCreative, // 行动号召按钮ID
                    logoLayoutId = R.id.flADLogo // 广告标识布局ID
                )

                // 绑定点击响应,使广告可以被点击
                adObject.registerViewForInteraction(
                    activity, // 上下文Activity
                    adView, // 广告视图
                    viewGroup, // 容器视图
                    binder // 视图绑定器
                )
            }
        } else {
            // 广告未准备就绪,记录日志
            logger.add("Native: 广告还未准备好")
        }
    }

    /**
     * 检查广告是否已加载
     * 
     * @return true表示广告对象已创建,false表示未创建
     * 注意:此方法仅检查广告对象是否存在,不保证广告已准备就绪
     * 要检查广告是否可展示,请使用isReady()方法
     */
    fun isLoaded(): Boolean {
        return nativeAdObject != null
    }

    /**
     * 销毁广告对象
     * 
     * 当不再需要广告时,调用此方法销毁广告对象,释放资源
     */
    fun destroy() {
        // 销毁广告对象
        nativeAdObject?.destroy()
        // 置空广告对象引用
        nativeAdObject = null
    }
}

最佳实践

1. 广告布局设计

  • 与应用风格一致:使用与应用相同的设计语言
  • 清晰的广告标识:确保用户能够识别这是广告
  • 合理的信息层次:标题、描述、品牌信息清晰可见
  • 响应式设计:适应不同屏幕尺寸

2. 优化建议

  • 预加载广告:在需要展示广告前提前加载
  • 缓存机制:实现广告缓存,提高展示速度
  • 交替布局:为不同广告创建多种布局
  • 性能优化:避免过度渲染和内存泄漏

3. 避免的问题

  • 不要隐藏广告标识
  • 不要修改广告内容
  • 不要干扰广告的正常展示
  • 不要在广告中添加无关内容

常见问题

Q: 原生广告为什么不显示?

A: 可能的原因:

  • 广告单元 ID 不正确
  • 网络连接问题
  • 广告库存不足
  • 布局问题(容器不可见或尺寸为 0)
  • 渲染代码有问题

Q: 如何提高原生广告的效果?

A: 建议:

  • 精心设计广告布局
  • 确保广告与应用内容相关
  • 优化广告加载和渲染
  • 测试不同的广告位置
  • 分析广告效果数据

Q: 原生广告可以在 RecyclerView 中使用吗?

A: 可以,实现方法:

  • 在 ViewHolder 中管理原生广告
  • 为广告项创建专门的布局
  • 正确处理广告的加载和渲染
  • 避免频繁创建和销毁广告

相关链接