You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Composable嵌入WebView切换底部导航栏时触发Fatal signal 11崩溃

解决Composable中WebView切换标签页崩溃问题

问题场景

我在Composable中嵌入了WebView,未在Activity代码中直接使用该WebView,仅让其在Composable中显示。应用底部导航栏包含A、B、C、D四个标签页,WebView位于A标签页。当从A标签页切换至D标签页,再切回A标签页时,应用崩溃,抛出以下错误:

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x20 in tid 23934 (RenderThread), pid 23898 (.mobile.android)
    12-08 19:29:21.885 22765 22765 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000020
    12-08 19:29:21.885 22765 22765 F DEBUG   : Cause: null pointer dereference
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x0  0000000000000000  x1  0000000000000002  x2  0000000000000000  x3  0000000000000001
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x4  0000000000000001  x5  0000000000000000  x6  0000000000000001  x7  0000000000000001
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x8  aeeabf0bf3658480  x9  aeeabf0bf3658480  x10 00000076cb15c000  x11 000000000000006d
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x12 0000000000000004  x13 0000000000000001  x14 0000000000000000  x15 0000000000000000
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x16 0000000000000001  x17 0000007a4dbb2ec0  x18 00000076ca0be000  x19 0000000000000000
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x20 0000000000000000  x21 00000076cb151740  x22 0000000000000000  x23 0000000000000000
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 00000077ce2ffb90
    12-08 19:29:21.885 22765 22765 F DEBUG   :     x28 0000000000000000  x29 00000076cb151630
    12-08 19:29:21.885 22765 22765 F DEBUG   :     lr  0000007a5bd18300  sp  00000076cb151630  pc  0000007a5be95b90  pst 0000000060000000
    12-08 19:29:21.885 22765 22765 F DEBUG   : backtrace:
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #00 pc 0000000000695b90  /system/lib64/libhwui.so (SkSurface::getCanvas()+16) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #01 pc 00000000005182fc  /system/lib64/libhwui.so (android::uirenderer::skiapipeline::GLFunctorDrawable::onDraw(SkCanvas*)+860) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #02 pc 00000000005a5634  /system/lib64/libhwui.so (SkDrawable::draw(SkCanvas*, SkMatrix const*)+116) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #03 pc 00000000008d6bd4  /system/lib64/libhwui.so (skgpu::v1::Device::drawDrawable(SkCanvas*, SkDrawable*, SkMatrix const*)+344) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #04 pc 00000000004f7d70  /system/lib64/libhwui.so (android::uirenderer::$_37::__invoke(void const*, SkCanvas*, SkMatrix const&)+136) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #05 pc 00000000004f4f54  /system/lib64/libhwui.so (android::uirenderer::DisplayListData::draw(SkCanvas*) const+128) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #06 pc 00000000004de5b8  /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::drawContent(SkCanvas*) const+1748) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5)
    12-08 19:29:21.885 22765 22765 F DEBUG   :       #07 pc 00000000004dece4  /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::forceDraw(SkCanvas*) 

问题代码

@Composable
private fun TestWebView(url: String, onReceivedError: (WebResourceError) -> Unit) {
    val scrollState = rememberScrollState()
     var mWebView: WebView? = null
   // var webView: WebView? by remember { mutableStateOf(null) }

    // State to track whether the WebView is loading or not
    var isLoading by remember { mutableStateOf(true) }
    Box(
        modifier = Modifier.fillMaxSize()
    ) {
        AndroidView(
            modifier = Modifier
                .fillMaxWidth()
                .verticalScroll(scrollState),
            factory = { context ->
                WebView(context).apply {
                    mWebView = WebView(context);
                    webViewClient = object : WebViewClient() {
                        override fun onReceivedError(
                            view: WebView?,
                            request: WebResourceRequest?,
                            error: WebResourceError?
                        ) {
                            super.onReceivedError(view, request, error)
                            error?.let { onReceivedError(it) }
                            isLoading = false
                        }

                        override fun shouldOverrideUrlLoading(
                            view: WebView?,
                            request: WebResourceRequest?
                        ): Boolean {
                            view?.loadUrl(request?.url.toString())
                            return true
                        }

                        override fun onRenderProcessGone(
                            view: WebView?,
                            detail: RenderProcessGoneDetail?
                        ): Boolean {
                            return false
                        }

                        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                            super.onPageStarted(view, url, favicon)
                            isLoading = true
                        }

                        override fun onPageFinished(view: WebView?, url: String?) {
                            super.onPageFinished(view, url)
                            isLoading = false
                        }
                    }
                    settings.domStorageEnabled = true
                    settings.javaScriptEnabled = true
                    settings.cacheMode = WebSettings.LOAD_DEFAULT
                    this.alpha = 0.99F
                    loadUrl(url)
                }
            },
            update = { webView ->
                webView.loadUrl(url)
            }
        )

        if (isLoading) {
            CircularProgressIndicator(
                modifier = Modifier
                    .size(30.dp)
                    .align(Alignment.Center)
            )
        }

// Dispose the WebView to avoid memory leaks when the composable is removed
        DisposableEffect(Unit) {
            onDispose {
                mWebView?.clearCache(true);
                mWebView?.clearHistory();
                mWebView?.onPause();
                mWebView?.removeAllViews();
                mWebView?.destroy();
                mWebView = null
            }
        }
    }
}

问题原因

  1. 重复创建WebView实例:在AndroidView的factory中,实际渲染的是WebView(context).apply{...}创建的实例,但你又额外通过mWebView = WebView(context)创建了一个新实例。导致DisposableEffect销毁的是未绑定到Composable的WebView,真正显示的WebView未被正确销毁,切换标签页后系统尝试渲染已失效的资源,引发RenderThread空指针崩溃。
  2. 未正确保存WebView引用:mWebView没有用remember修饰,每次Composable重组都会被重置为null,可能导致DisposableEffect无法获取有效实例进行销毁。
  3. 不必要的重复加载:update块每次都会调用loadUrl(url),即使URL未变化,引发不必要的状态波动。

修复方案

1. 修正WebView实例创建与引用

将mWebView改为用remember保存的可变状态,直接绑定AndroidView创建的实例,删除重复创建代码:

var mWebView: WebView? by remember { mutableStateOf(null) }

在factory的apply块中设置mWebView = this(this即为当前创建的WebView实例)。

2. 优化update块逻辑

仅当URL变化时才重新加载:

update = { webView ->
    if (webView.url != url) {
        webView.loadUrl(url)
    }
}

3. 调整DisposableEffect依赖

让DisposableEffect依赖mWebView,确保仅当WebView存在时执行销毁操作:

DisposableEffect(mWebView) {
    onDispose {
        mWebView?.let { webView ->
            webView.clearCache(true)
            webView.clearHistory()
            webView.onPause()
            webView.removeAllViews()
            webView.destroy()
            mWebView = null
        }
    }
}

修复后的完整代码

@Composable
private fun TestWebView(url: String, onReceivedError: (WebResourceError) -> Unit) {
    val scrollState = rememberScrollState()
    var mWebView: WebView? by remember { mutableStateOf(null) }

    var isLoading by remember { mutableStateOf(true) }
    Box(modifier = Modifier.fillMaxSize()) {
        AndroidView(
            modifier = Modifier
                .fillMaxWidth()
                .verticalScroll(scrollState),
            factory = { context ->
                WebView(context).apply {
                    mWebView = this
                    webViewClient = object : WebViewClient() {
                        override fun onReceivedError(
                            view: WebView?,
                            request: WebResourceRequest?,
                            error: WebResourceError?
                        ) {
                            super.onReceivedError(view, request, error)
                            error?.let { onReceivedError(it) }
                            isLoading = false
                        }

                        override fun shouldOverrideUrlLoading(
                            view: WebView?,
                            request: WebResourceRequest?
                        ): Boolean {
                            view?.loadUrl(request?.url.toString())
                            return true
                        }

                        override fun onRenderProcessGone(
                            view: WebView?,
                            detail: RenderProcessGoneDetail?
                        ): Boolean {
                            return false
                        }

                        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                            super.onPageStarted(view, url, favicon)
                            isLoading = true
                        }

                        override fun onPageFinished(view: WebView?, url: String?) {
                            super.onPageFinished(view, url)
                            isLoading = false
                        }
                    }
                    settings.domStorageEnabled = true
                    settings.javaScriptEnabled = true
                    settings.cacheMode = WebSettings.LOAD_DEFAULT
                    alpha = 0.99F
                    loadUrl(url)
                }
            },
            update = { webView ->
                if (webView.url != url) {
                    webView.loadUrl(url)
                }
            }
        )

        if (isLoading) {
            CircularProgressIndicator(
                modifier = Modifier
                    .size(30.dp)
                    .align(Alignment.Center)
            )
        }

        DisposableEffect(mWebView) {
            onDispose {
                mWebView?.let { webView ->
                    webView.clearCache(true)
                    webView.clearHistory()
                    webView.onPause()
                    webView.removeAllViews()
                    webView.destroy()
                    mWebView = null
                }
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者prat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 23:50:05