当我们的webview 中嵌入了小红书等app 的分享页面后,一般页面上会展示“打开App”按钮。但如果什么都不做,是没法打开指定App 的,甚至小红书的按钮还会不断的下载小红书apk。
添加scheme白名单
app 允许跳转的scheme 需要在配置文件中先声明。
Android
<manifest ...> <queries> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="xhsdiscover" /> </intent> </queries> </manifest>
iOS
<key>LSApplicationQueriesSchemes</key> <array> <string>xhsdiscover</string> </array>
WebView允许所有请求
如果不配置originWhitelist,你会发现除了http 开头的链接,其他的都拦截不到。
<WebView ref={webViewRef} source={{ uri: 'https://www.zhihu.com' }} onMessage={handleMessage} // 监听 JS 发送的消息 injectedJavaScript={injectedJavaScript} // 注入 JS 代码 onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest} originWhitelist={['*']} // 允许所有请求 />
拦截请求
在onShouldStartLoadWithRequest 函数中拦截请求,检查是否是scheme
const handleShouldStartLoadWithRequest = (request: any) => { const { url: targetUrl } = request; if (!targetUrl) return false; // 拦截 scheme:// 跳转 if (targetUrl.startsWith('xiaohongshu://') || targetUrl.startsWith('otherapp://')) { if (hasUserInteracted) { Linking.openURL(targetUrl).catch(err => console.error('Failed to open URL:', err)); } return false; // 关键:拦截 WebView 内跳转 } return true; // 其他正常请求放行 };
自动跳转
这个问题也处理了我好久,理论上我们是点击“打开app” 再去打开。但很多第三方页面,在页面加载之后,会立即自动跳转,不确定是设计如此,还是调用了检测app 是否安装的方法,触发了跳转。
常见的唤端方式:在主文档中location.href=”xxx://xxx” 或window.open() 打开。
还有一些可能用iframe 来检测是否安装了对应的app
<iframe src="yourapp://open" style="display:none;"></iframe>
如果 App 已安装,iframe
的 src
触发 App 打开。 如果 App 未安装,iframe
可能会抛出错误(但部分浏览器不会有明显反应)。
如果检测失败的话,多数会有一个降级的H5 页面来承接“打开app” 按钮的跳转。
因此,在我们拦截了自动跳转之后,网页内部可能认为已经失败了,下次点击“打开app” 会走降级逻辑。这不符合我们的诉求。
目前我的实现是,先拦截自动跳转并存储scheme, 在下次拦截到跳转的降级url 时,替换为scheme。 这块继续探索有没有更好的方案。
打开app
Linking.openURL(targetUrl).catch(err => console.error('Failed to open URL:', err));