react-native-webview: Android is crashing when "WebView" is wrapped by "View"

Bug description: I have an autosized webview that works perfectly on ios:

import React, { useState } from "react";
import { View } from "react-native";
import { WebView } from "react-native-webview";
import PropTypes from "prop-types";

const BODY_TAG_PATTERN = /<\/ *body>/;

const code = `
    <style>
        body, html {
            margin: 0;
            padding: 0;
        }
    </style>

    <script>
        window.ReactNativeWebView.postMessage(document.body.scrollHeight, '*');
    </script>
`;

const injectCode = (html) => {
    return html.replace(BODY_TAG_PATTERN, `${code}</body>`);
};

const WebViewAutoHeight = ({ html, style }) => {
    const [contentHeight, setContentHeight] = useState(1);

    const adjustHeight = (event) => {
        setContentHeight(parseInt(event.nativeEvent.data));
    };

    if (!html) {
        throw new Error("At this moment, WebViewAutoHeight only supports html files");
    }

    if (!BODY_TAG_PATTERN.test(html)) {
        throw new Error(`Cannot find </body> from: ${html}`);
    }

    return (
        <View style={[style, { height: contentHeight }]}>
            <WebView
                source={{ html: injectCode(html) }}
                onMessage={adjustHeight}
            />
        </View>
    );
};

WebViewAutoHeight.propTypes = {
    html: PropTypes.string.isRequired,
    style: PropTypes.any
};

WebViewAutoHeight.defaultProps = {
    style: null
};

export default WebViewAutoHeight;

However, when I try to use it on android, the app crashes without a message. Then I decided to remove everything and replaced the render method with the following:

<View>
   <WebView
        source={{ uri: 'https://infinite.red' }}
        style={{ marginTop: 20 }}
   />
</View>

but it was still crashing. Then I removed the outer view element and it worked!

Expected behavior: The android app should not crash

Environment:

  • OS: macOS Mojave
  • OS version: 10.14.5
  • react-native version: 0.59.8
  • react-native-webview version: 6.9.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 6
  • Comments: 40 (1 by maintainers)

Commits related to this issue

Most upvoted comments

Just spent far too long working on a solution for this. I didn’t get any of the above solutions to work, however setting androidHardwareAccelerationDisabled={true} on the WebView did work.

@Rendfold I managed to come across this link in my research: https://www.gitmemory.com/issue/react-native-community/react-native-webview/430/493976111

Apperently this bug can be fixed by changing the Opacity of the webviwe to 0.99 instead of the default value of 1. Setting the overflow value to “hidden” prevents that the background will be visible.

This worked for me as a fix Altho the androud crash did only appeare when I pressed the back button and by this dismissed the screen. I am also using react navigation which probably played a role as well.

<View
   renderToHardwareTextureAndroid={true}>
   <WebView
        source={{ uri: 'https://infinite.red' }}
        style={{ marginTop: 20 }}
   />
</View>

In my case, I fixed it by adding renderToHardwareTextureAndroid={true} on it’s parent View.

I had the same issue and it worked to set the opacity to 0.99 - Absolutely ridiculous that this works however, makes absolutely no sense.

We use a hidden webview in our authentication process. After logging in, we navigate the user to another screen. Then we get this crash on android. We are using react-navigation v5.

We were already using the androidHardwareAccelerationDisabled prop fix until upgrading React Native from 63 to 66.

At v66, none of the above solutions in this issue worked. However, we noticed that our wrapper componentWillUnmount lifecycle method wasn’t getting called.

So our solution was to have our parent screen component listen to react-navigation events: transitionEnd & blur to keep a hasNavigationTransitioned boolean:

useEffect(() => navigation.addListener('transitionEnd', () => {
  setHasNavigationTransitioned(true);
}), [navigation, setHasNavigationTransitioned]);

useEffect(() => navigation.addListener('blur', () => { 
  setHasNavigationTransitioned(false);
}), [navigation, setHasNavigationTransitioned]);

Then, use that boolean to conditionally render the WebView:

            <View style={styles.hideMe}>
                {hasNavigationTransitioned ? (
                    <WebView
                        source={{ uri }}
                        androidHardwareAccelerationDisabled
                    />
                ) : null}
            </View>

I think this resolves some race condition, and allows the webview to be properly disposed on android.

@jonsaich and @pmm1 solutions work for me but i prefer androidHardwareAccelerationDisabled={true}. Less error prone, some developer can be confused about the opacity solution and accidentaly remove it…

Updating: well androidHardwareAccelerationDisabled={true} has a huge trade-off in perfomance in my case, now i’m using the opacity solution…

I’m on 11.13.0 and none of these fixes work 😢 I see this issue was closed but I don’t see any real resolved issue with it? The opacity fix and hardwareAcceleration fix don’t work, neither does disabling animation on the react navigation screen…

Using androidLayerType="software" solves the problem, but performance becomes horrible this way. Another workaround is to unmount the webview when leaving the screen, but this comes with its own problems.

Can confirm that applying styles { opacity: 0.99, overflow: 'hidden' } to the WebView fixed this issue for me.

androidHardwareAccelerationDisabled={true} stopped the crashing but the video was black. You could press the play button but not see the video at all.

Very peculiar…

What was the solution?

opacity=0.99 or react-navigation animationEnabled=false works for me. androidLayerType="software" has a very bad impact on performances.

useEffect(() => navigation.addListener('transitionEnd', () => {
  setHasNavigationTransitioned(true);
}), [navigation, setHasNavigationTransitioned]);

useEffect(() => navigation.addListener('blur', () => { 
  setHasNavigationTransitioned(false);
}), [navigation, setHasNavigationTransitioned]);

Thanks @BDDev the solution works This should be a big problem to address, crash still happen in 2023

I have the same issue and tried all the mentionned solutions, but only the androidLayerType=“software” shared by @Tracer1337 worked. Thanks!

WebView with style={{ opacity: 0.99, overflow: ‘hidden’ }} worked for me too, thanks for this thread!

Solved by using flex:1 in View and WebView style.

using androidLayerType="software" or androidHardwareAccelerationDisabled={true} seem to fix it,

However that seems to cause issues when trying to render 3d objects inside the webview.

Workaround was to hide the webview component while the screen is loading. And show it after a 500ms delay.

My uneducated guess is that it might be something to do with the screen animation when it enters the view.

setting style={{ opacity: 0.99, overflow: 'hidden' }} on the webview fixes the crash on android for us as well

I managed to fix this by setting view style to, webview ^11.17.1

        <View
          style={{
            height: window.height,
            width: window.width,
            maxHeight: '100%',
          }}>
          <WebView source={{uri: route.params.url}} />
        </View>

In our case, androidHardwareAccelerationDisabled={true} worked, while opacity: 0.99 did not. We have had to apply this fix to all webviews in our app. This issue was easiest to reproduce on LG Stylo 6 and Google Pixel devices.

<View
   renderToHardwareTextureAndroid={true}>
   <WebView
        source={{ uri: 'https://infinite.red' }}
        style={{ marginTop: 20 }}
   />
</View>

In my case, I fixed it by adding renderToHardwareTextureAndroid={true} on it’s parent View.

This is what worked for us with the least perf hit. Thanks @sooakim!

For those who are big suffered by this problem! I’ve tried all of the above remedies, but none of them work in RN 0.69 and React Navigation 6. So I’ve used “transitionEnd” event of React Navigation, I’ve solved this. Below is my worked code.

// React component class file constructor(props) { super(props) this.state = { isWebViewLoaded: false, }

props.navigation.addListener(‘transitionEnd’, () => { this.setState({ isWebViewLoaded: true }) })

props.navigation.addListener(‘beforeRemove’, () => { this.setState({ isWebViewLoaded: false }) }) }

render() { return ( <View style={{ flex: 1, position: ‘relative’ }}> {this.state.isWebViewLoaded && ( <WebView source={{ uri: ‘https://google.com/’, headers: { ‘Cache-Control’: ‘no-cache’ }, }} renderError={() => { return ( <View style={{ flex: 1, alignItems: ‘center’, justifyContent: ‘center’, }}

Network connection error.

) }} /> )}

)

// package.json file “@react-navigation/native”: “^6.0.11”, “@react-navigation/native-stack”: “^6.7.0”, “@react-navigation/stack”: “^6.2.2”, “react”: “18.2.0”, “react-native”: “0.69.1”, “react-native-gesture-handler”: “^2.5.0”, “react-native-screens”: “^3.15.0”, “react-native-webview”: “^11.22.7”

Just spent far too long working on a solution for this. I didn’t get any of the above solutions to work, however setting androidHardwareAccelerationDisabled={true} on the WebView did work.

Thank you, its work, save my time.