You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Jetpack Compose WebView加载m.bilibili.com样式不完整的问题排查与修复咨询

Jetpack Compose WebView加载m.bilibili.com样式不完整的问题排查与修复咨询

嘿,我来帮你搞定这个问题!我之前也碰过WebView加载特定站点样式异常的情况,尤其是像B站这种对移动端环境比较敏感的站点,咱们一步步来排查和修复。

一、核心问题推测:User-Agent不匹配导致资源加载被拦截

B站的移动端站点会根据请求的User-Agent(UA)返回适配的资源,如果WebView默认的UA和手机系统浏览器的UA差异太大,B站可能会拒绝加载部分样式表,或者返回非移动端的适配资源,这是这类问题最常见的诱因。

二、必要的WebSettings补充配置

你的现有配置只开了基础的JS和DOM存储,但还有几个关键配置没加,这些会直接影响资源加载和页面渲染逻辑:

  1. 适配移动端的User-Agent设置
    直接复用系统浏览器的UA,或者设置一个和你三星S24系统浏览器完全一致的UA(可以通过系统浏览器访问本地检测页面获取),让B站认为当前是正常的移动端浏览器请求:

    // 用系统默认UA,和你的三星S24浏览器保持一致
    settings.userAgentString = WebSettings.getDefaultUserAgent(context)
    
  2. 开启自适应屏幕的关键配置
    这两个设置能让WebView正确识别移动端页面的视口规则,避免样式错位或资源加载范围异常:

    settings.useWideViewPort = true // 支持自适应视口宽度
    settings.loadWithOverviewMode = true // 按屏幕宽度自动缩放加载页面
    
  3. 补充资源加载相关配置
    加上这些配置,覆盖B站可能用到的交互和资源加载规则:

    settings.javaScriptCanOpenWindowsAutomatically = true // 支持JS打开新窗口,适配B站的部分交互
    settings.cacheMode = WebSettings.LOAD_DEFAULT // 遵循默认缓存策略,避免重复请求被拦截
    settings.mediaPlaybackRequiresUserGesture = false // 允许自动播放媒体(B站移动端部分内容依赖)
    

三、WebViewClient的优化与排查手段

你当前的WebViewClient是空实现,我们可以加一些日志排查逻辑,确认是哪些样式表没被请求,以及页面加载的状态:

webViewClient = object : WebViewClient() {
    // 打印所有CSS请求,排查是否有遗漏
    override fun shouldInterceptRequest(
        view: WebView?,
        request: WebResourceRequest?
    ): WebResourceResponse? {
        request?.url?.let { url ->
            if (url.path.contains(".css")) {
                println("检测到CSS请求: ${url.toString()}")
            }
        }
        return super.shouldInterceptRequest(view, request)
    }

    // 页面加载完成后检查已加载的样式表数量
    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)
        view?.evaluateJavascript("document.querySelectorAll('link[rel=stylesheet]').length") { count ->
            println("已成功加载的样式表数量: $count")
        }
    }
}

通过这些日志,你能快速确认是B站没返回样式表,还是WebView没触发请求,或者请求被拦截了。

四、代码逻辑的小优化

你代码里的LaunchedEffect加了2秒延迟完全没必要,而且直接用webViewSource!!有潜在空指针风险,咱们把这部分简化:

LaunchedEffect(Unit) {
    // 去掉无意义的延迟,用安全调用避免空指针
    webViewSource?.let { it.loadUrl("https://m.bilibili.com/") }
}

五、完整修复后的代码

最后把所有修改整合起来,给你一个可直接用的版本:

@Composable
fun WebViewPage() {
    println("[recomposable] - WebViewPage")
    var webViewSource: WebView? by remember { mutableStateOf(null) }

    LaunchedEffect(Unit) {
        webViewSource?.let { it.loadUrl("https://m.bilibili.com/") }
    }

    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            WebView(context).apply {
                // Cookie配置提前初始化
                val cookieManager = CookieManager.getInstance()
                cookieManager.setAcceptThirdPartyCookies(this, true)
                cookieManager.setAcceptCookie(true)

                settings.apply {
                    // 保留原有基础配置
                    javaScriptEnabled = true
                    domStorageEnabled = true
                    mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
                    loadsImagesAutomatically = true

                    // 新增的关键配置
                    userAgentString = WebSettings.getDefaultUserAgent(context)
                    useWideViewPort = true
                    loadWithOverviewMode = true
                    javaScriptCanOpenWindowsAutomatically = true
                    cacheMode = WebSettings.LOAD_DEFAULT
                    mediaPlaybackRequiresUserGesture = false
                }

                webViewClient = object : WebViewClient() {
                    override fun shouldInterceptRequest(
                        view: WebView?,
                        request: WebResourceRequest?
                    ): WebResourceResponse? {
                        request?.url?.let { url ->
                            if (url.path.contains(".css")) {
                                println("检测到CSS请求: ${url.toString()}")
                            }
                        }
                        return super.shouldInterceptRequest(view, request)
                    }

                    override fun onPageFinished(view: WebView?, url: String?) {
                        super.onPageFinished(view, url)
                        view?.evaluateJavascript("document.querySelectorAll('link[rel=stylesheet]').length") { count ->
                            println("已成功加载的样式表数量: $count")
                        }
                    }
                }

                loadUrl("about:blank")
            }.also { webViewSource = it }
        },
        update = { /* 不需要额外更新逻辑,留空即可 */ }
    )
}

最后再提几个排查小技巧

  1. 用Chrome远程调试WebView:在Chrome地址栏输入chrome://inspect,找到你的App对应的WebView,就能像调试普通网页一样查看控制台错误、网络请求详情,快速定位是样式表403了,还是JS报错导致样式没渲染。
  2. 完全匹配系统UA:如果还是不行,你可以把WebView的UA改成你三星S24系统浏览器的真实UA,进一步规避B站的特征检测。

试试这些修改,应该就能解决样式不完整的问题啦!如果还有问题,咱们再接着排查~

火山引擎 最新活动