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

Expo WebView注入JS在Android无法加载,iOS正常求解决

问题描述

我使用Expo SDK开发应用,因Intercom仅支持原生应用,故通过Expo-WebView展示Intercom聊天功能。该功能在iOS上可正常运行,但Android端无法启动,仅显示蓝色背景。

使用版本:

  • Expo 48.0
  • React Native 0.72
  • react-native-webview 11.26

相关代码:

import React, { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react';
import WebView, { WebViewMessageEvent } from 'react-native-webview';
import { LoadingSpinner } from '~components/styled/LoadingSpinner';
import { COLORS } from '~styles/theme';
import * as Notifications from 'expo-notifications';
import {
  registerForPushNotificationsAsync,
  sendPushNotification,
} from '../../utils/PushNotification';

export const IntercomWebView: FunctionComponent<{
  uuid: string;
  email: string;
  firstName: string;
  lastName: string;
}> = ({ uuid, email, firstName, lastName }) => {
  const [isLoading, setIsLoading] = useState(true);
  const [expoPushToken, setExpoPushToken] = useState<string>('');
  const [, setNotification] = useState<Notifications.Notification>();
  const notificationListener = useRef<Notifications.Subscription>();
  const responseListener = useRef<Notifications.Subscription>();

  useEffect(() => {
    registerForPushNotificationsAsync().then((token) => {
      if (token) {
        setExpoPushToken(token.data);
      }
    });

    notificationListener.current = Notifications.addNotificationReceivedListener((notification) => {
      if (notification) {
        setNotification(notification);
      }
    });

    responseListener.current = Notifications.addNotificationResponseReceivedListener((response) => {
      console.log('response', response);
    });

    return () => {
      if (notificationListener.current) {
        Notifications.removeNotificationSubscription(notificationListener.current);
      }
      if (responseListener.current) {
        Notifications.removeNotificationSubscription(responseListener.current);
      }
    };
  }, []);

  const inject = `
    var style = document.createElement('style');
    style.innerHTML = '[aria-label=&quot;Close&quot;] { display: none !important; }';
    document.head.appendChild(style);
    window.Intercom('boot', {
      api_base: 'intercom-url',
      app_id: 'intercom-id',
      email: &quot;${email}&quot;,
      name: &quot;${firstName} ${lastName}&quot;,
      user_id: &quot;${uuid}&quot;,
    });
    true; // note: this is required, or you'll sometimes get silent failures
  `;

  const handlePushNotification = useCallback((event: WebViewMessageEvent) => {
    const notificationData = JSON.parse(event.nativeEvent.data);
    const { title, message } = notificationData;
    sendPushNotification(expoPushToken, title, message);
  }, [expoPushToken]);

  return (
    <>
      {isLoading && <LoadingSpinner size=&quot;small&quot; />}
      <WebView
        style={{ flex: 1, backgroundColor: COLORS.Blue }}
        originWhitelist={['*']}
        source={{
          html: `<!doctype html>
            <html lang=&quot;en&quot;>
              <head>
                <meta charset=&quot;UTF-8&quot; />
                <meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; />
                <title>Intercom Chat</title>
                <script>
                  (function(){
                    var w=window;
                    var ic=w.Intercom;
                    if(typeof ic===&quot;function&quot;)
                      ic('reattach_activator');
                      ic('update',w.intercomSettings);
                    }else{
                      var d=document;
                      var i=function(){i.c(arguments);};
                      i.q=[];
                      i.c=function(args){i.q.push(args);};
                      w.Intercom=i;
                      var l=function(){
                        var s=d.createElement('script');
                        s.type='text/javascript';
                        s.async=true;
                        s.src='intercom-url';
                        var x=d.getElementsByTagName('script')[0];
                        x.parentNode.insertBefore(s,x);
                      };
                      if(document.readyState==='complete'){
                        l();
                      }else if(w.attachEvent){
                        w.attachEvent('onload',l);
                      }else{
                        w.addEventListener('load',l,false);
                      }
                    }
                  })();
                </script>
              </head>
              <body>
                <script>
                  (function(){
                    window.Intercom('show');
                  })();
                </script>
              </body>
            </html>`,
        }}
        javaScriptEnabled={true}
        javaScriptEnabledAndroid={true}
        injectedJavaScript={inject}
        injectedJavaScriptForMainFrameOnly={true}
        onMessage={(event) => {}}
      />
    </>
  );
};

已尝试操作:

  • 添加WebView配置:
    setDomStorageEnabled={true}
    setLoadWithOverviewMode={true}
    setUseWideViewPort={true}
    setBuiltInZoomControls={true}
    setDisplayZoomControls={false}
    setSupportZoom={true}
    setJavaScriptEnabled={true}
    setDefaultTextEncodingName={'utf-8'}
    
  • 在app.config.ts中配置Android权限:
    android: {
        permissions: ['INTERNET'],
    
  • 测试加载普通URL正常,推测Android无法处理注入的JavaScript。

解决思路

1. 调整JavaScript注入时机

Android WebView对脚本注入时机要求更严格,injectedJavaScript可能在Intercom脚本加载完成前执行,导致window.Intercom未定义。改用onLoadEnd回调+injectJavaScript方法,确保Intercom脚本加载完成后再执行初始化逻辑:

// 先创建WebView引用
const webViewRef = useRef<WebView>(null);

// 替换原WebView的injectedJavaScript,添加onLoadEnd回调
<WebView
  // ...其他原有属性
  ref={webViewRef}
  onLoadEnd={() => {
    const injectScript = `
      var style = document.createElement('style');
      style.innerHTML = "[aria-label='Close'] { display: none !important; }";
      document.head.appendChild(style);
      window.Intercom('boot', {
        api_base: 'intercom-url',
        app_id: 'intercom-id',
        email: "${email}",
        name: "${firstName} ${lastName}",
        user_id: "${uuid}",
      });
      window.Intercom('show');
    `;
    webViewRef.current?.injectJavaScript(injectScript);
  }}
/>

2. 修复转义字符问题

代码中使用的&quot;转义双引号在Android WebView中可能解析异常,直接改用单引号包裹字符串或模板字符串原生双引号:

// 原写法
style.innerHTML = '[aria-label=&quot;Close&quot;] { display: none !important; }';
// 修改为
style.innerHTML = "[aria-label='Close'] { display: none !important; }";
// 或
style.innerHTML = `[aria-label="Close"] { display: none !important; }`;

3. 禁用injectedJavaScriptForMainFrameOnly属性

Android WebView对该属性的支持存在兼容性问题,先移除该配置,测试脚本是否能正常注入。

4. 补充Android专属WebView配置

添加以下配置提升兼容性:

<WebView
  // ...其他属性
  allowUniversalAccessFromFileURLs={true}
  allowFileAccess={true}
  mixedContentMode={'compatibility'} // 若Intercom资源包含HTTP协议,Android默认会阻止加载
  onMessage={(event) => console.log('WebView Debug:', event.nativeEvent.data)} // 开启调试日志
/>

5. 检查Intercom脚本加载状态

在注入脚本中添加日志检查,确保Intercom脚本加载成功:

const injectScript = `
  console.log('Intercom Status:', typeof window.Intercom);
  if (typeof window.Intercom !== 'function') {
    // 手动重新加载Intercom脚本
    var s = document.createElement('script');
    s.type='text/javascript';
    s.async=true;
    s.src='intercom-url';
    var x = document.getElementsByTagName('script')[0];
    x.parentNode.insertBefore(s,x);
    // 延迟执行初始化
    setTimeout(() => {
      window.Intercom('boot', {...});
    }, 1000);
  } else {
    // 正常初始化逻辑
    window.Intercom('boot', {...});
  }
`;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 08:21:02