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
相关产品推荐
相关产品推荐

