移除 android 反应本机 webview 上的默认错误页面

Remove android default error page on react native webview

我在 React Native 中使用 webview,我想在发生某些错误时显示自定义错误消息(例如,没有互联网连接)。

我的代码:

<WebView 
  renderError={() => (
    <MissingConnection />
  )}
  other params....
/>

当加载网页出错时,webview 会在几分之一秒内显示默认的 android 错误,例如: 然后我的 MissingConnection 组件弹出,隐藏了 webview。

有没有办法完全删除默认的 android 错误屏幕?只闪了几分之一秒,结果感觉真的不对。

首先:你做得对

我最近观察到同样的问题并做了一些调查。问题不在于您的代码或 React Native 或 react-native-webview.

这只是 Android 的 WebView 的默认行为。很多Java开发者遇到同样的问题,SO上相关线程的例子:

webview error while loading a page without internet

Prevent WebView from displaying "web page not available"

Android WebView onReceivedError()

通常的解决方法是:

  • 在尝试加载任何内容之前检查互联网连接(防止失败)

  • 快速删除错误内容并在onReceivedError中显示您自己的内容(基本上映射到react-native-webview中的renderError方法)。有时加载本地 url 就像在 Java here.

  • 中所做的那样
  • 注意有一个覆盖层,如果完全没有错误就将其删除。 react-native-webview 反其道而行之,出现错误时显示叠加层。但是 Activity 指示器叠加层是一个很好的例子,它会一直显示直到加载完成或遇到错误。

据我所知,除了这些令人失望的方式,我们无能为力,因为我不想与系统作斗争。

编辑:Android 的 Firefox Focus 在错误处理程序中快速替换内容。

这是在 Java 中完成的,来源如下:

https://github.com/mozilla-mobile/focus-android/blob/c789362b9c331b2036755a8398e3770be43b50b8/app/src/main/java/org/mozilla/focus/webview/FocusWebViewClient.java#L330

https://github.com/mozilla-mobile/focus-android/blob/63339d2d9a5d132bf4a1fffc4c46c0ce393abe87/app/src/main/java/org/mozilla/focus/webview/ErrorPage.java#L126.

所以我想我们是好伙伴!

编辑 2:我很好奇在真正的 Android 设备上不处于调试模式时这是否真的可见。我有根据的猜测是代码执行得更快而且根本不应该是可见的。顺便说一句,此页面可能仅针对 404(未找到)错误显示,如果您使用硬编码 urls 和您自己的服务器,则这种情况不太可能发生。

编辑 3:本机错误页面 在发布模式下在真实设备上可见运行。防止这种闪烁的唯一方法是创建一个覆盖层。我在 https://github.com/react-native-community/react-native-webview/issues/474#issuecomment-487022106.

处打开了一个与另一个错误相关的问题,该错误也用 react-native-webview 解决了这个问题

我的解决方案是警报功能

import React, { Component } from 'react';
import { Alert } from 'react-native';
import { View, Spinner } from 'native-base';
import { WebView } from 'react-native-webview';

export default class ExampleScreen extends Component {

  displaySpinner() {
    return (
      <View style={{ flex: 1 }}>
        <Spinner color="blue" />
      </View>
    );
  }

  displayError() {
    Alert.alert(
      "no_internet",
      "require_internet_connection",
      [
        { text: 'OK', onPress: () => this.props.navigation.goBack() },
      ],
      { cancelable: false });
  }

  render() {
    return (
      <WebView onError={() => this.displayError()}
        startInLoadingState={true}
        renderLoading={() => {
          return this.displaySpinner();
        }}
        source={{ uri: 'https://example.com' }} />
    );
  }
};

实际上,就我而言,这个解决方案很管用

import React, { useEffect, useRef } from 'react';
import { StyleSheet, View, BackHandler } from 'react-native';
import { colors, variables } from 'utils/theme';
import { WebView } from 'react-native-webview';
import { Button, Spinner, Text } from 'components';
import { fa } from 'utils/constants/locales';

const uri = YOUR_WEB_PAGE_URL

const Loading = () => {
  return (
    <View style={styles.loadingWrapper}>
      <Spinner />
      <Text style={styles.loading}>{fa.proEducation.loading}</Text>
    </View>
  );
};
const Error = ({ reload }) => {
  return (
    <View style={styles.loadingWrapper}>
      <Button
        style={styles.retry}
        label={fa.proEducation.retry}
        primary
        onPress={reload}
      />
    </View>
  );
};

const ProEducation = () => {
  const webview = useRef(null);
  const canGoBackRef = useRef(false);
  const onAndroidBackPress = () => {
    if (canGoBackRef.current && webview.current) {
      webview.current.goBack();
      return true;
    }
    return false;
  };
  useEffect(() => {
    BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress);
    return () => {
      BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress);
    };
  }, []);
  const onNavigationStateChange = ({ canGoBack }) => {
    canGoBackRef.current = canGoBack;
  };
  const reload = () => webview.current.reload();

  return (
    <View style={styles.wrapper}>
      <WebView
        ref={webview}
        source={{ uri }}
        style={styles.webView}
        onNavigationStateChange={onNavigationStateChange}
        javaScriptEnabled
        domStorageEnabled
        renderLoading={() => <Loading />}
        renderError={() => <Error reload={reload} />}
        startInLoadingState
      />
    </View>
  );
};

const styles = StyleSheet.create({
  loading: {
    color: colors.lightBlack,
    fontSize: 15,
    marginTop: 8,
    textAlign: 'center',
  },
  loadingWrapper: {
    backgroundColor: colors.white,
    bottom: 0,
    flex: 1,
    justifyContent: 'center',
    left: 0,
    marginBottom: 'auto',
    marginLeft: 'auto',
    marginRight: 'auto',
    marginTop: 'auto',
    position: 'absolute',
    right: 0,
    top: 0,
  },
  retry: {
    alignSelf: 'center',
    paddingHorizontal: variables.gutter,
    paddingVertical: variables.gutter / 2,
  },
  webView: {
    flex: 1,
  },
  wrapper: {
    backgroundColor: colors.bg,
    flex: 1,
  },
});

export default ProEducation;