compose-webview-multiplatform icon indicating copy to clipboard operation
compose-webview-multiplatform copied to clipboard

Exception in newer Android

Open ShmuelCammebys opened this issue 3 months ago • 3 comments

Library version: 2.0.3, Android device: 10, Kotlin 2.0.21, latest dependencies.

Card(Modifier.widthIn(max = 300.dp), RoundedCornerShape(16.dp), cardColors) {
  if (html != null) {
    if (fullWebView) WebView(
      html
        .formatHtmlForSurfaceColor(cardContainerColor)
        .also {
          ld { "Loading HTML: $it" }
        },
        Modifier.heightIn(max = 500.dp)
      )
    } 
    else Text(html)
}
@Composable
actual fun WebView(html: String, modifier: Modifier) {
    val state = rememberWebViewStateWithHTMLData(data = html)
    com.multiplatform.webview.web.WebView(state = state, modifier = modifier)
}
2025-11-05 16:09:19.379  9498-9498  myapp                   com...myapp  D  Loading HTML: <ul><li>test</li></ul>
2025-11-05 16:09:19.418  9498-9498  WebViewFactory          com...myapp  I  Loading com.google.android.webview version 141.0.7390.122 (code 739012230)
2025-11-05 16:09:19.557  9498-9498  WebViewFactory          com...myapp  E  error instantiating provider (Ask Gemini)
                                                                                                    java.lang.reflect.InvocationTargetException
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:266)
                                                                                                    	at android.webkit.CookieManager.getInstance(CookieManager.java:50)
                                                                                                    	at com.multiplatform.webview.cookie.AndroidCookieManager.<clinit>(AndroidCookieManager.kt:16)
                                                                                                    	at com.multiplatform.webview.cookie.AndroidCookieManagerKt.WebViewCookieManager(AndroidCookieManager.kt:162)
                                                                                                    	at com.multiplatform.webview.web.WebViewState.<init>(WebViewState.kt:101)
                                                                                                    	at com.multiplatform.webview.web.WebViewStateKt.rememberWebViewStateWithHTMLData(WebViewState.kt:213)
                                                                                                    	at myapp.ui.components.common.html.WebView_androidKt.WebView(WebView.android.kt:9)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.MessageCard$lambda$4$2(MessageCard.kt:144)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.$r8$lambda$5lhIKeq6gu30ew-0iT_AQysDllA(Unknown Source:0)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt$$ExternalSyntheticLambda8.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.kt:130)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke$lambda$0(ComposableLambda.kt:132)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.$r8$lambda$qz3voikrQeNn5XJEtUlXR2wfzBw(Unknown Source:0)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl$$ExternalSyntheticLambda16.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:196)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2895)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.skipToGroupEnd(Composer.kt:3289)
                                                                                                    	at androidx.compose.material3.CardKt.Card(Card.kt:79)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.MessageCard(MessageCard.kt:137)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.MessageCard$lambda$5(Unknown Source:6)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.$r8$lambda$jIpA4lsJgUiJ7XrhhdYuv_S1CdM(Unknown Source:0)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt$$ExternalSyntheticLambda9.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:196)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2895)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:3231)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.doCompose-aFTiNEg(Composer.kt:3855)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.recompose-aFTiNEg$runtime(Composer.kt:3779)
                                                                                                    	at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:1075)
                                                                                                    	at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1373)
                                                                                                    	at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:156)
                                                                                                    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2.invokeSuspend$lambda$22(Recomposer.kt:627)
                                                                                                    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2.$r8$lambda$OqADLCDYmRw1RgNUvn1CR0kX32M(Unknown Source:0)
                                                                                                    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:39)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:108)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                                                                                                    	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1008)
                                                                                                    	at android.view.Choreographer.doCallbacks(Choreographer.java:809)
                                                                                                    	at android.view.Choreographer.doFrame(Choreographer.java:740)
--------- beginning of crash
2025-11-05 16:09:19.558  9498-9498  WebViewFactory          com...myapp  E  	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995) (Ask Gemini)
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:938)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                    	at android.os.Looper.loop(Looper.java:246)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8653)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
                                                                                                    Caused by: java.lang.RuntimeException: Package not found: com.google.android.webview
                                                                                                    	at android.webkit.WebViewDelegate.getPackageId(WebViewDelegate.java:191)
                                                                                                    	at com.android.webview.chromium.WebViewChromiumFactoryProvider.<init>(chromium-TrichromeWebViewGoogle.aab-stable-739012230:404)
                                                                                                    	at com.android.webview.chromium.WebViewChromiumFactoryProviderForR.<init>(chromium-TrichromeWebViewGoogle.aab-stable-739012230:1)
                                                                                                    	at com.android.webview.chromium.WebViewChromiumFactoryProviderForR.create(chromium-TrichromeWebViewGoogle.aab-stable-739012230:3)
                                                                                                    	... 49 more
2025-11-05 16:09:19.579  9498-9498  AndroidRuntime          com...myapp  E  FATAL EXCEPTION: main (Ask Gemini)
                                                                                                    Process: com.myapp, PID: 9498
                                                                                                    java.lang.ExceptionInInitializerError
                                                                                                    	at com.multiplatform.webview.cookie.AndroidCookieManagerKt.WebViewCookieManager(AndroidCookieManager.kt:162)
                                                                                                    	at com.multiplatform.webview.web.WebViewState.<init>(WebViewState.kt:101)
                                                                                                    	at com.multiplatform.webview.web.WebViewStateKt.rememberWebViewStateWithHTMLData(WebViewState.kt:213)
                                                                                                    	at myapp.ui.components.common.html.WebView_androidKt.WebView(WebView.android.kt:9)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.MessageCard$lambda$4$2(MessageCard.kt:144)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.$r8$lambda$5lhIKeq6gu30ew-0iT_AQysDllA(Unknown Source:0)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt$$ExternalSyntheticLambda8.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.kt:130)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke$lambda$0(ComposableLambda.kt:132)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.$r8$lambda$qz3voikrQeNn5XJEtUlXR2wfzBw(Unknown Source:0)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl$$ExternalSyntheticLambda16.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:196)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2895)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.skipToGroupEnd(Composer.kt:3289)
                                                                                                    	at androidx.compose.material3.CardKt.Card(Card.kt:79)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.MessageCard(MessageCard.kt:137)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.MessageCard$lambda$5(Unknown Source:6)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt.$r8$lambda$jIpA4lsJgUiJ7XrhhdYuv_S1CdM(Unknown Source:0)
                                                                                                    	at myapp.ui.components.domain.MessageCardKt$$ExternalSyntheticLambda9.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:196)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2895)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:3231)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.doCompose-aFTiNEg(Composer.kt:3855)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.recompose-aFTiNEg$runtime(Composer.kt:3779)
                                                                                                    	at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:1075)
                                                                                                    	at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1373)
                                                                                                    	at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:156)
                                                                                                    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2.invokeSuspend$lambda$22(Recomposer.kt:627)
                                                                                                    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2.$r8$lambda$OqADLCDYmRw1RgNUvn1CR0kX32M(Unknown Source:0)
                                                                                                    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:39)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:108)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                                                                                                    	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1008)
                                                                                                    	at android.view.Choreographer.doCallbacks(Choreographer.java:809)
                                                                                                    	at android.view.Choreographer.doFrame(Choreographer.java:740)
                                                                                                    	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995)
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:938)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                    	at android.os.Looper.loop(Looper.java:246)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8653)
2025-11-05 16:09:19.580  9498-9498  AndroidRuntime          com...myapp  E  	at java.lang.reflect.Method.invoke(Native Method) (Ask Gemini)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
                                                                                                    	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@a2e45a3, androidx.compose.ui.platform.MotionDurationScaleImpl@dfd80a0, StandaloneCoroutine{Cancelling}@de0b459, AndroidUiDispatcher@576181e]
                                                                                                    Caused by: android.util.AndroidRuntimeException: java.lang.reflect.InvocationTargetException
                                                                                                    	at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:271)
                                                                                                    	at android.webkit.CookieManager.getInstance(CookieManager.java:50)
                                                                                                    	at com.multiplatform.webview.cookie.AndroidCookieManager.<clinit>(AndroidCookieManager.kt:16)
                                                                                                    	... 45 more
                                                                                                    Caused by: java.lang.reflect.InvocationTargetException
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:266)
                                                                                                    	... 47 more
                                                                                                    Caused by: java.lang.RuntimeException: Package not found: com.google.android.webview
                                                                                                    	at android.webkit.WebViewDelegate.getPackageId(WebViewDelegate.java:191)
                                                                                                    	at com.android.webview.chromium.WebViewChromiumFactoryProvider.<init>(chromium-TrichromeWebViewGoogle.aab-stable-739012230:404)
                                                                                                    	at com.android.webview.chromium.WebViewChromiumFactoryProviderForR.<init>(chromium-TrichromeWebViewGoogle.aab-stable-739012230:1)
                                                                                                    	at com.android.webview.chromium.WebViewChromiumFactoryProviderForR.create(chromium-TrichromeWebViewGoogle.aab-stable-739012230:3)
                                                                                                    	... 49 more

ShmuelCammebys avatar Nov 05 '25 21:11 ShmuelCammebys

@KevinnZou The issue lies in this line: https://github.com/KevinnZou/compose-webview-multiplatform/blob/ed86a62809bea429a2b80ded716accc6f647b502/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt#L101

That initialization runs as soon as WebViewState is constructed (i.e., during composition when you call rememberWebViewStateWithHTMLData(...)). Inside WebViewCookieManager() (per the stack trace), the Android CookieManager.getInstance() is touched in a static initializer. If that happens before WebView’s provider is safely available (e.g., secondary process without data directory suffix, or too early in app startup, or not on the UI thread), Android fails with WebViewFactory ... error instantiating provider.

Fix

Instead of constructing the cookie manager in the property initializer, make it lazy and only touch it after a real WebView exists (and on the main thread).

class WebViewState(
    webContent: WebContent,
) {
    // ... existing properties ...

    // Defer construction, and ensure we are on the main thread when accessed
    val cookieManager: CookieManager by lazy(LazyThreadSafetyMode.NONE) {
        ensureMainThread()
        WebViewCookieManager()
    }

    private fun ensureMainThread() {
        check(Looper.getMainLooper() == Looper.myLooper()) {
            "CookieManager must be accessed on the main (UI) thread"
        }
    }

Then, only access state.cookieManager from places that run on the UI thread after you’ve created the actual Android WebView (e.g., inside AndroidView(factory = { ... }) block).

AndroidView(
    factory = { context ->
        WebView(context).apply {
            // The provider is materialized here; now it’s safe to touch CookieManager.
            // Trigger lazy init explicitly after WebView exists:
            state.cookieManager // this invokes the lazy block
        }
    },
    update = { /* ... */ }
)

ShmuelCammebys avatar Nov 05 '25 21:11 ShmuelCammebys

This also works:

@Composable
fun SafeWebView(html: String, modifier: Modifier) {

    var mount by rememberSaveable { mutableStateOf(false) }

    // Wait one frame before composing the library's WebView
    LaunchedEffect(Unit) {
        // Suspend until the next frame
        withFrameNanos { /* no-op */ }
        mount = true
    }

    if (mount) {
        lv { "Mounting" }
        val state = rememberWebViewStateWithHTMLData(html)
        com.multiplatform.webview.web.WebView(state = state, modifier = modifier)
    }

}

ShmuelCammebys avatar Nov 05 '25 22:11 ShmuelCammebys

@ShmuelCammebys Thanks for your suggestion, just feel free to submit a PR. I will review and merge it.

KevinnZou avatar Nov 10 '25 23:11 KevinnZou