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方法名

*/

相关推荐
Larcher几秒前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐13 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭25 分钟前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu1 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花1 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程