Jetpack Compose集成Admob横幅广告 解决LazyColumn滚动重载问题
问题根因
LazyColumn 默认会回收移出可视区域、超出缓存阈值的列表项。原有实现将AdView实例的初始化、广告加载逻辑全部耦合在列表项内部的AndroidView factory中,列表项被回收时对应的AdView实例会被直接销毁。当滚动回到广告位置时,列表项重建会触发factory生成全新的AdView实例,必然导致广告重新加载,既浪费广告请求配额,也可能降低填充收益。
修复方案
核心逻辑是将AdView实例的生命周期与列表项解绑,对齐页面级生命周期:列表滚动回收时仅临时断开AdView与列表项容器的关联,不销毁AdView实例;回滚到广告位置时直接复用已加载完成的AdView,避免重复请求。
具体实现步骤:
- 在
LazyColumn作用域外通过remember持有AdView实例,广告加载逻辑仅在实例初始化时执行一次 - 给承载广告的列表项设置固定唯一key,避免列表复用导致的位置错乱
- 将AdView与页面生命周期绑定,仅在页面退出时执行广告销毁逻辑
- 列表项内的
AndroidView直接复用外部持有的AdView实例,禁止在factory中新建AdView
完整实现代码:
@Composable fun MainView(){ val context = LocalContext.current // 页面级持有AdView实例,生命周期和页面绑定,不受列表回收影响 val bannerAdView = remember { AdView(context).apply { setAdSize(AdSize.BANNER) adUnitId = context.getString(R.string.adId) // 仅初始化时加载一次广告 loadAd(AdRequest.Builder().build()) adListener = object : AdListener() { override fun onAdLoaded() { // 广告加载成功逻辑 } } } } // 绑定生命周期,避免内存泄漏 val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner, bannerAdView) { lifecycleOwner.lifecycle.addObserver(bannerAdView) onDispose { lifecycleOwner.lifecycle.removeObserver(bannerAdView) // 仅页面退出时销毁广告 bannerAdView.destroy() } } LazyColumn{ // 其他列表内容 // 给广告item设置固定唯一key item(key = "fixed_banner_ad_position") { BannerAdView(adView = bannerAdView) } // 其他列表内容 } } @Composable fun BannerAdView(adView: AdView) { AndroidView( modifier = Modifier.fillMaxWidth(), factory = { adView }, // 直接复用传入的已初始化AdView update = { /* 不需要额外处理,AdView会自动处理重新挂载后的展示 */ } ) }
注意事项
- 不要在
AndroidView的factory、update回调中调用loadAd(),广告仅需在AdView首次初始化时加载一次,AdView原生支持detach后重新attach的状态恢复 - 不要在列表项滚出可视区域时调用AdView的
pause()、destroy()方法,这类生命周期方法仅在整个页面退出时触发即可 - 若列表内嵌入多个横幅广告位,需要为每个广告位提前持有独立的AdView实例,禁止同一个AdView实例挂载到多个列表项位置
内容的提问来源于stack exchange,提问作者Mayur
相关产品推荐
相关产品推荐

