jsBridge接入流程

发布于:2025-09-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到