Search code examples
androidandroid-webviewandroid-jetpack-compose

How to recycle a webview on jetpack compose?


I have an app that emulates ViewPager behavior via HorizontalPager. Each Pager has a WebView, and the ViewPager will release WebView for pages that are far from the current page. What should I do in Jetpack Compose? It seems the composable should not be destroyed


Solution

  • This is expected behaviour. ViewPager is a lazy view and it's not gonna store non visible views.

    In clear Compose all you need to cache is scrolling position and some other state variables, and Compose will do that.

    But when you're using AndroidView, you had to manage it by yourself. I'm not sure if it's possible to retrieve WebView scrolling position, but even if you can - the page still will be re-loaded.

    A possible solution is storing WebViews in a state like this:

    val webViews = remember(LocalContext.current) { mutableStateMapOf<Int, WebView>() }
    val state = rememberPagerState()
    LaunchedEffect(state.currentPage) {
        webViews.keys.forEach { key ->
            val maxCacheDistance = 3
            if (abs(key - state.currentPage) >= maxCacheDistance) {
                webViews.remove(key)
                println("webView $key deinited")
            }
        }
    }
    HorizontalPager(
        count = 10,
        state = state,
    ) { page ->
        val url = "https://duckduckgo.com/?q=$page"
        AndroidView(
            factory = { context -> 
                webViews[page] ?: run {
                    WebView(context).also { webView ->
                        webView.settings.javaScriptEnabled = true
                        webViews[page] = webView
                    }
                }
            },
            update = { webView ->
                if (webView.url != url) {
                    webView.loadUrl(url)
                }
            },
            modifier = Modifier.fillMaxSize()
        )
    }