JSBridge 问题总结

1,210 阅读3分钟

客户端(安卓/ios)如何给前端对接

大致架构就是,原生客户端相当于一个壳,通过Webview嵌套着含有主要内容的H5,客户端对数据进行管理,如数据缓存或数据接口请求,然后将数据处理好传给前端,而前端主要是将数据以各种方式显示出来 。

WebViewJavascriptBridge is undefined,找不到Bridge对象

  • 查找问题: 在Hybrid开发中,一般使用的是第三方的bridge通信库,这种库可以使原生和前端webview进行信息传递和方法的互调,采用的是JSBridge库,原生端会在webview的window对象中插入一个WebViewJavaScriptBridge对象,这个对象就是可以用call和register两大方法来和原生通信的关键对象。
  • 解决过程分析: 排除手机系统问题,模拟器问题,XML布局问题等
  • 最终解决方案: webViewClient也要设置成BridgeWebViewClient对象的overide!

js调用原生方法会偶现失败?

具体的表现: js调用native方法时shouldOverridgeUrlLoading没有被触发,因此端上没有去刷新JS调用的message queue.

JsBridge的异步不执行的处理--promise异步变同步

背景: 在进行后端接口请求的时候统一先把参数返给APP端,APP端进行加密以后,在返回给前端,前端再进行异步请求。 这里涉及到异步操作,异步请求可能会在和APP交互以前执行,那样就得不到APP返回的加密参数就执行了请求,因此得不到数据。 解决方案: 利用promise把APP,H5和后端之间的交互进行同步处理,一步一步进行交互操作。

  • web端的bridge.js
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
var isiOS = !!u.match(/(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
// alert('是不是Android:'+isAndroid);
// alert('是不是iOS:'+isiOS);
function setupWebViewJavascriptBridge(callback) {
  if(isAndroid){
    if (window.WebViewJavascriptBridge) {
    callback(WebViewJavascriptBridge)
    } else {
      document.addEventListener('WebViewJavascriptBridgeReady', function (event) {
          if(window.onWebViewJavascriptBridgeReady) window.onWebViewJavascriptBridgeReady(window.__bridge = WebViewJavascriptBridge);
          callback(WebViewJavascriptBridge)
      }, false)
    }
  }
  
  if(isiOS){
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
  }
  
}

export default {
  callhandler (name, data, callback) {
    setupWebViewJavascriptBridge(function(bridge) {
      bridge.callHandler(name, data, callback)
    })
  },
  registerhandler (name, callback) {
    setupWebViewJavascriptBridge(function (bridge) {
      bridge.registerHandler(name, function (data, responseCallback) {
        callback(data, responseCallback)
      })
    })
  }
}

  • 封装和APP交互的方法callBridge.js

export default {
    methods:{
        callBridgefn(json){ // 调用app加密方法
            let p = new Promise((resolve, reject)=>{
                this.$bridge.callhandler('encryptParams', JSON.stringify(json),(data) =>{  //encryptParams是APP端规定的方法名
                
resolve(data) })
           }) return p; 
       }, 
    goBack(){ // 调用APP的返回 
      this.$bridge.callhandler('goBack', (data) =>{ // goBack是APP端规定的方法名 
    }) 
  } 
  } 
}

  • 利用promise重写axios异步请求方法promise.js
import axios from 'axios'

export default{
    methods:{
     sendServer(param) { 
        return new Promise((resolve,reject)=>{            
                axios.post(url ,param).then(res=>{ // url为请求地址
                resolve(res.data)
            }).catch(err=>{
                reject(err)
            })
        })
     },
    }
}
  • 在页面中请求接口的方法
_sendServer(param){
this.callBridgefn(param).then(data=>{ 
  this.sendServer(JSON.parse(data))
   .then(res=>{ 

   // 这里写要对后端返回的数据进行的操做 }) })
}   

  • 在页面初始化的时候调用
created(){
     this._sendServer(param) //param是要传递给APP进行加密的参数
}

JSBridge并发调用问题

问题: 同时并发发送多个请求,native端只能拦截到最后一次请求,前面的请求没法拦截。 改用location.href="scheme"这种方式是可以让native端都能拦截到,但是会产生内部webview后退时,请求不了native。 解决方法: 思路

  • 假设JSBridge提供了方法call()
  • 每次调用call时,会把当前所有的数据缓存在一个数组变量sendMessage里面
  • 每次调用都会发送一个固定的scheme,
  • native接受这个消息后,会调用js的一个固定全局方法pullMessage(),该方法返回值是sendMessage数组里面的所有数据的json数组字符串
  • native拿到js方法返回的JSON数组字符串进行解析,然后针对每个调用进行分发处理,然后在进行回调相应的js方法。

项目问题

背景:app(A)页面跳转到h5(B)页面,然后H5(B)页面跳转到H5(C)页面,这时C页面无法调用app内的方法,但是在B页面是可以调用的。 思路: 让iOS调用我们的标识,然后在调用我们的方法,传参数过来,我们在对数据进行处理

$(documnet).ready(function(){
  location.href ="aaaa://showMobile",// 一个标识
})
function jsonStr(str){
  $('.lottery_content h5').html(str)
}