jsBridge接入流程
import deviceInfo from './deviceInfo'
import { setRefreshToken } from './token'
// === 设备判断 ===
const u = navigator.userAgent
export const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
export const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
export const isNativeMobile = (isAndroid || isIOS) && new URLSearchParams(window.location.search).get('native')
// === 调试信息 ===
console.log('=== 设备检测 ===', {
userAgent: u,
isAndroid,
isIOS,
isNativeMobile,
urlParams: window.location.search
})
// === nativeTokenReady: 外部可 await 等待 token 注入 ===
let nativeTokenReadyResolve: (_token: string) => void
export const nativeTokenReady: Promise<string> = new Promise((resolve) => {
nativeTokenReadyResolve = resolve
})
// === 桥接状态管理 ===
let bridgeInitialized = false
let tokenReceived = false
/**
* 安卓桥函数:需要 bridge.init()
*/
const androidFunction = (callback: any) => {
console.log('=== 安卓桥函数调用 ===', new Date().toISOString())
if (window.WebViewJavascriptBridge) {
console.log('=== 安卓桥已存在,直接回调 ===')
callback(window.WebViewJavascriptBridge)
} else {
console.log('=== 安卓桥不存在,等待WebViewJavascriptBridgeReady事件 ===')
document.addEventListener('WebViewJavascriptBridgeReady', () => {
console.log('=== 收到WebViewJavascriptBridgeReady事件 ===')
callback(window.WebViewJavascriptBridge)
}, false)
}
}
/**
* iOS 桥函数:用 iframe 触发注入
*/
const iosFunction = (callback: any) => {
console.log('=== iOS桥函数调用 ===', new Date().toISOString())
if (window.WebViewJavascriptBridge) {
console.log('=== iOS桥已存在,直接回调 ===')
return callback(window.WebViewJavascriptBridge)
}
if (window.WVJBCallbacks) {
console.log('=== iOS桥回调已存在,添加到队列 ===')
return window.WVJBCallbacks.push(callback)
}
console.log('=== 创建iOS桥回调队列和iframe ===')
window.WVJBCallbacks = [callback]
const WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'
document.documentElement.appendChild(WVJBIframe)
setTimeout(() => {
document.documentElement.removeChild(WVJBIframe)
console.log('=== 移除iOS桥iframe ===')
}, 0)
}
/**
* 处理token注入
*/
const handleTokenInjection = (data: any) => {
console.log('=== 处理token注入 ===', new Date().toISOString())
console.log('原始数据:', data)
console.log('数据类型:', typeof data)
try {
let res = data
if (isAndroid && typeof data === 'string') {
try {
res = JSON.parse(data)
console.log('安卓JSON解析成功:', res)
} catch (e) {
console.error('安卓JSON解析失败:', e, data)
return
}
}
console.log('处理后的数据:', res)
console.log('数据字段:', Object.keys(res || {}))
// 尝试多种可能的token字段
const validToken = res['token']
if (validToken) {
console.log('=== 设置token成功 ===', validToken)
setRefreshToken(validToken)
tokenReceived = true
nativeTokenReadyResolve(validToken)
} else {
console.warn('=== 没有找到有效的token ===')
// 即使没有token也要resolve,避免无限等待
if (!tokenReceived) {
tokenReceived = true
nativeTokenReadyResolve('')
}
}
// 设置设备版本(如果有的话)
if (res?.['Device-Version']) {
console.log('=== 设置设备版本 ===', res['Device-Version'])
deviceInfo.setdeviceVersion(res['Device-Version'])
}
} catch (error) {
console.error('=== 处理token注入失败 ===', error, data)
if (!tokenReceived) {
tokenReceived = true
nativeTokenReadyResolve('')
}
}
}
// === 导出的统一桥接初始化函数 ===
export function setupBridge(): any {
if (bridgeInitialized) {
console.log('=== 桥接已初始化,跳过重复初始化 ===')
return
}
console.log('=== 开始初始化桥接 ===', new Date().toISOString())
bridgeInitialized = true
window.setupWebViewJavascriptBridge = isAndroid ? androidFunction : iosFunction
window.setupWebViewJavascriptBridge((bridge) => {
console.log('=== 桥接回调执行 ===', new Date().toISOString())
console.log('桥接对象:', bridge)
// 注册原生注入 refreshToken 的方法
bridge.registerHandler('injectRefreshToken', handleTokenInjection)
console.log('=== 已注册injectRefreshToken处理器 ===')
// 安卓需要调用 bridge.init()
if (isAndroid) {
console.log('=== 调用安卓bridge.init ===')
bridge.init((_msg: any, responseCallback: any) => {
console.log('=== 安卓bridge.init回调 ===', _msg)
responseCallback('H5 已收到')
})
}
})
}
// === 封装 callHandler 调用 ===
export const bridge = {
callHandler: (methodName: string, params?: any, callback?: any): any => {
console.log('=== 调用桥接方法 ===', methodName, params)
if (window?.setupWebViewJavascriptBridge) {
window.setupWebViewJavascriptBridge((bridge) => {
bridge.callHandler(methodName, params || null, (data: any, fn: any) => {
console.log('=== 桥接方法回调 ===', methodName, data)
callback?.(data, fn)
})
})
} else {
console.warn('=== 桥接未初始化,无法调用方法 ===', methodName)
}
}
}
// === 初始化桥接 ===
if (isNativeMobile) {
console.log('=== 检测到原生环境,开始初始化 ===', new Date().toISOString())
// 立即初始化
setupBridge()
// 监听全局事件(兜底方案)
const messageHandler = (event: any) => {
console.log('=== 收到message事件 ===', new Date().toISOString(), event.data)
// 检查是否是token相关的事件
if (event.data && (event.data.token || event.data.refreshToken || event.data.type === 'injectRefreshToken')) {
console.log('=== 通过message事件收到token ===', event.data)
handleTokenInjection(event.data)
}
}
window.addEventListener('message', messageHandler)
// 延迟初始化(兜底)
setTimeout(() => {
if (!tokenReceived) {
console.log('=== 延迟初始化桥接 ===', new Date().toISOString())
setupBridge()
}
}, 1000)
// 超时处理
setTimeout(() => {
if (!tokenReceived) {
console.warn('=== 10秒内未收到token,可能存在问题 ===')
nativeTokenReadyResolve('')
}
}, 10000)
}
/**
* app携带地址栏参数
* native=true
* theme=light | dark
*
* bridge方法名
*/