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="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}", }); 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="small" />} <WebView style={{ flex: 1, backgroundColor: COLORS.Blue }} originWhitelist={['*']} source={{ html: `<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Intercom Chat</title> <script> (function(){ var w=window; var ic=w.Intercom; if(typeof ic==="function") 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. 修复转义字符问题
代码中使用的"转义双引号在Android WebView中可能解析异常,直接改用单引号包裹字符串或模板字符串原生双引号:
// 原写法 style.innerHTML = '[aria-label="Close"] { 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
相关产品推荐
相关产品推荐

