You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose实现单屏仅加载一次广告及解决导航刷新问题

问题描述

屏幕导航时广告会不断刷新,之前试过用标记判断广告是否已加载,但视图刷新后标记会变回默认值,根本解决不了问题,想找个能让单屏只加载一次广告的办法。

现有代码

Compose 实现代码

AndroidView(
        modifier = modifier,
        factory = {
            val view =
                LayoutInflater.from(context).inflate(R.layout.native_ad, null, true)
            val adView = view.findViewById<TemplateView>(R.id.medium_ad_template)
            
            adView.apply {
                AdLoader.Builder(context, context.getString(AR.string.native_unit_id))
                    .forNativeAd { nativeAd ->
                        setStyles(NativeTemplateStyle.Builder().build())
                        setNativeAd(nativeAd)
                    }
                    .build()
                    .loadAd(request)
            }

            view
        }
    )

native_ad.xml 布局文件

<LinearLayout 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="match_parent">

    <com.google.android.ads.nativetemplates.TemplateView
        android:id="@+id/medium_ad_template"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:gnt_template_type="@layout/gnt_medium_template_view" />
</LinearLayout>
解决方案

核心解决思路就是把广告加载的状态和实例从AndroidView的factory方法里抽出来,让状态在Compose界面重新渲染时保持住,别每次视图重建就重新加载广告。

方法一:用remember存储广告状态和实例

在Compose里用remember来保存广告是否已加载的标记,以及加载好的NativeAd实例,这样界面重新渲染时状态不会丢失:

@Composable
fun NativeAdView(modifier: Modifier = Modifier) {
    // 用remember保存广告是否已加载的状态,默认是未加载
    val isAdLoaded = remember { mutableStateOf(false) }
    var nativeAd: NativeAd? by remember { mutableStateOf(null) }
    val context = LocalContext.current

    AndroidView(
        modifier = modifier,
        factory = {
            val view = LayoutInflater.from(context).inflate(R.layout.native_ad, null, true)
            val adView = view.findViewById<TemplateView>(R.id.medium_ad_template)
            
            // 只有广告没加载的时候才发起请求
            if (!isAdLoaded.value) {
                AdLoader.Builder(context, context.getString(AR.string.native_unit_id))
                    .forNativeAd { ad ->
                        nativeAd = ad
                        adView.setStyles(NativeTemplateStyle.Builder().build())
                        adView.setNativeAd(ad)
                        isAdLoaded.value = true // 标记成已加载
                    }
                    .build()
                    .loadAd(request)
            } else {
                // 如果已经有加载好的广告,直接设置到视图上
                nativeAd?.let {
                    adView.setStyles(NativeTemplateStyle.Builder().build())
                    adView.setNativeAd(it)
                }
            }

            view
        },
        update = { view ->
            // 视图更新的时候,如果有现成的广告,重新设置一遍(防止视图重建后广告消失)
            val adView = view.findViewById<TemplateView>(R.id.medium_ad_template)
            nativeAd?.let {
                adView.setStyles(NativeTemplateStyle.Builder().build())
                adView.setNativeAd(it)
            }
        }
    )
}

方法二:把广告逻辑放到ViewModel里

如果这个广告是和页面绑定的,把广告加载状态放到ViewModel中,这样页面重建(比如导航返回、屏幕旋转)时状态还能保留:

class AdViewModel : ViewModel() {
    val isAdLoaded = mutableStateOf(false)
    var nativeAd: NativeAd? = null

    fun loadAd(context: Context, request: AdRequest) {
        if (!isAdLoaded.value) {
            AdLoader.Builder(context, context.getString(AR.string.native_unit_id))
                .forNativeAd { ad ->
                    nativeAd = ad
                    isAdLoaded.value = true
                }
                .build()
                .loadAd(request)
        }
    }
}

// 在Compose页面中使用
@Composable
fun AdScreen(viewModel: AdViewModel = viewModel()) {
    val context = LocalContext.current

    // 页面第一次进入时加载广告,只会执行一次
    LaunchedEffect(Unit) {
        viewModel.loadAd(context, AdRequest.Builder().build())
    }

    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = {
            val view = LayoutInflater.from(context).inflate(R.layout.native_ad, null, true)
            view.findViewById<TemplateView>(R.id.medium_ad_template)
        },
        update = { adView ->
            // 广告加载完成后,设置到视图上
            viewModel.nativeAd?.let {
                adView.setStyles(NativeTemplateStyle.Builder().build())
                adView.setNativeAd(it)
            }
        }
    )
}

关键点说明

  • remember能让状态在Compose界面重新渲染时保持,不会每次都重置标记。
  • LaunchedEffect(Unit)保证广告加载逻辑只在页面首次进入时执行一次。
  • ViewModel的方式更适合页面重建的场景,状态会被ViewModel妥善保存,不会因为导航或配置变更丢失。

内容的提问来源于stack exchange,提问作者Unes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:10:26