6

I am wrapping an AdManagerAdView in an AndroidView so I can use it in Jetpack Compose. The image fails to load when I use it in a LazyColumn AND the AdManagerAdView tries to load the image before the composable is on screen.

If I scroll quickly to that element, so LazyColumn composes it AND it is on screen before the image comes back from the ad server, it works as expected.

LazyColumn {
        items(5) {
            SomeOtherComposable(it)
        }

        item {
            AndroidView(
                modifier = Modifier
                    .width(300.dp)
                    .height(250.dp)
                    .background(Color.Green),
                factory = { context ->
                    val adView = AdManagerAdView(context)
                    adView.adSize = AdSize.MEDIUM_RECTANGLE
                    adView.adUnitId = adUnitId
                    adView.loadAd(Builder().build())
                    adView
                }
            )
        }

        items(5) {
            SomeOtherComposable(it)
        }
}

For demo purposes...

@Composable
fun SomeOtherComposable(i: Int) {
    Text(
        text = "SomeOtherComposable $i",
        modifier = Modifier
            .fillMaxWidth()
            .height(320.dp)
            .background(Color.White)
    )
}

This also works fine if the wrapped AdManagerAdView is used in a non-lazy Column or any other Compose layout.

This feels like a weird timing thing in LazyColumn that just happens to manifest when the Composable isn't on screen yet since using it in a regular Column works fine under the same scenario.

Has anyone experienced anything like this?

SOLVED See my answer below

Kelly Merrell
  • 1,245
  • 1
  • 10
  • 16
  • Tried running your code with `adUnitId = "/6499/example/banner"` and a test add loads fine. – Phil Dukhov Mar 31 '22 at 03:12
  • Thanks for giving it a shot, @PylypDukhov, but I am able to reproduce it 100% of the time using the example adUnitId. It is tricky timing, which complicates things. You need to scroll until the Composable is called, but stop scrolling until the image has finished loading (which you can't really see since it is off screen), then scroll it into view to verify the behavior. Using my sample code, this means scrolling until "SomeOtherComposable 4" is on screen, then waiting a 2-5 seconds before continuing to scroll. – Kelly Merrell Mar 31 '22 at 20:03

1 Answers1

4

Ok, the issue is actually that both factory{} and update{} are called before the AndroidView has gone through the layout pass. In my steps to reproduce, the ad image is coming back and being added to the internal view before it has a measured width and height.

The solution is to delay that load call until after the layout pass using doOnLayout{} like so:

AndroidView(
        modifier = Modifier
            .width(300.dp)
            .height(250.dp)
            .background(Color.Green),
        factory = { context ->
            val adView = AdManagerAdView(context)
            adView.adSize = AdSize.MEDIUM_RECTANGLE
            adView
        },
        update = { adView ->
            adView.adUnitId = adUnitId
            adView.doOnLayout {
                adView.loadAd(Builder().build())
            }
        }
    )
Kelly Merrell
  • 1,245
  • 1
  • 10
  • 16
  • Oh can you explain how did you find out about when are factory and update called, I would like to know more about that. – Otto Cheley May 17 '22 at 22:03
  • Sorry about the late reply, but I just put some temporary logging in place to identify the order of the various calls. – Kelly Merrell Oct 14 '22 at 20:56