Compose WebView Crash

박주현·2025년 3월 26일

문제

상품 상세에서 Column안에 Native와 상품정보 WebView Component를 같이 사용해서 표시하는 구조로 되어있는데, 특정 상품에서 스크롤을 하게되면 Fatal signal 11 (SIGSEGV) 오류가 발생하면서 앱이 종료되는 이슈가 있었다.

해당 상품은 WebView컨텐츠에 이미지가 유독 많은 상품이였는데, 단순히 이미지가 많아서 Crash가 발생했다고 하기에는 다른 웹뷰로만 구성된 컨텐츠에는 더 많은 이미지가 리스트로 구성되어있는 케이스가 많았기 때문에 단순히 이미지 개수의 문제라고 생각되진 않았다.

과정

Fatal signal 11 오류가 뭔지 먼저 찾아보았다.
"Segmentation Fault 오류로, 메모리를 잘못 참조한 경우에 발생합니다." 라고 설명한다.

일반적인 WebView와는 다른점을 비교해봤다.

  • 전체 화면이 아닌 부분 화면을 WebView로 사용한다.
  • 스크롤은 웹뷰 내 스크롤이나 LazyColumn 방식이 아닌 Column에 verticalScroll을 적용한다.
  • WebView 컨텐츠가 큰 경우 verticalScroll시 웹뷰가 reload되고 있었다.

뭔가 위 상황들때문에 WebView 길이가 길어지면서 로드시간이 늘어나고 스크롤시 WebView가 reload되면서 메모리에 올라간 WebView를 찾지 못해 메모리를 잘못 참조해 Crash가 발생한게 아닌가 추측했고, 처음에 WebView를 AndroidView를 Box로 감싸기도 해보고
검색해본 여러 해결 방안들을 시도(largeHeap 설정, WebViewSetting, 하드웨어 가속등)했지만 이슈가 해소되지 않았다.

그러던 중 아래 글
https://wbrawner.com/2024/08/28/android-webview-crash-in-jetpack-compose/
을 찾아 해결하게 되었는데

WebView에 FrameLayout을 씌워 해결하는 방법이었다.

해결

기존 코드는 아래와 같이 되어있었는데

AndroidView(
        modifier = modifier,
        factory = {
            WebView(it).apply {
            	layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
                )
                settings.run {
                    javaScriptEnabled = true
                    useWideViewPort = true
                    loadWithOverviewMode = true
                    loadsImagesAutomatically = true
                    allowContentAccess = true
                    mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
                    cacheMode = WebSettings.LOAD_NO_CACHE
                    overScrollMode = View.OVER_SCROLL_NEVER
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                        setLayerType(View.LAYER_TYPE_HARDWARE, null)
                    }
                    textZoom = 100
                }
                webViewClient = object : WebViewClient() {...}
                webChromeClient = object : WebChromeClient() {...}
                loadUrl(url)
            }
        },
        update = {
            it.loadUrl(url)
        }
    )

다음과 같이 변경해 주었다.

AndroidView(
        modifier = modifier,
        factory = {
            FrameLayout(it).apply {
                addView(
                    WebView(it).apply {
                        tag = "webView" //framelayout에서 구분할 이름
                        layoutParams = ViewGroup.LayoutParams(
                            ViewGroup.LayoutParams.MATCH_PARENT,
                            ViewGroup.LayoutParams.WRAP_CONTENT
                        )
                        settings.run {
                            javaScriptEnabled = true
                            useWideViewPort = true
                            loadWithOverviewMode = true
                            loadsImagesAutomatically = true
                            allowContentAccess = true
                            mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
                            cacheMode = WebSettings.LOAD_NO_CACHE
                            overScrollMode = View.OVER_SCROLL_NEVER
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                                setLayerType(View.LAYER_TYPE_HARDWARE, null)
                            }
                            textZoom = 100
                        }
                        webViewClient = object : WebViewClient() {...}
                        webChromeClient = object : WebChromeClient() {...},
        update = {
            it.findViewWithTag<WebView>("webView").apply {
                loadUrl(url)
            }
        }

이렇게 FrameLayout으로 감싸서 WebView를 구성해주니 컨텐츠가 커도 정상적인 동작을 할 수 있었다.

아직 정확한 내부 메커니즘이 어떻게 동작해서 FrameLayout으로 감쌀경우 오류가 발생하지 않는지 모르겠다.(댓글로 알려주시면 감사드립니다)
Compose WebView는 사용할때마다 다양한 오류가 발생하는데 2년이 넘도록 사용했지만 아직 알아야할게 더 많은 것 같다.

같은 이슈를 겪고있는 분들이 있으시다면 도움이 되셨길 바랍니다. 감사합니다.

profile
Android Developer

0개의 댓글