JSBridge是个啥

直接来重点,记住:JSBridge 是一个很简单的东西,更多的是一种形式、一种思想,为了解决 H5 和 Native 的双向通信

就像我们刚接触 ajax 时,也很懵逼。其实,他们俩个差不多,ajax 是浏览器和服务器通信的规范(暂且叫规范,像 CMD 规范一样,SeaJS 是它的一种实现方式), JSBridge 是 H5 和 Native 通信的规范。axios 是 ajax 通信的一种实现方式,WebViewJavascriptBridge(下文要说) 是 JSBridge 的一种实现方式。明白了这些,下面就很好理解了。

回到顶部

H5 和 Native 的双向通信通用方法

H5通信方式和兼容性如下表所示。指的是借助 Native 的 webview 加载H5页面,H5 和 Native 之间通过注入API、URL拦截、全局调用等形式,实现消息通信。站在大厂的角度考虑,在实战的时候,会选择更兼容的方式。

H5调用Native方法

平台 方法 备注
Android shouldOverrideUrlLoading scheme拦截方法
Android addJavascriptInterface API
Android onJsAlert()、onJsConfirm()、onJsPrompt()
IOS 拦截URL
IOS JavaScriptCore API方法,IOS7+ 支持
IOS window.webkit.messageHandlers APi方法,IOS8+支持

1.注入 API 方式的主要原理:通过 WebView 提供的接口,向 JavaScript 的 Context(window)中注入对象或者方法,让 JavaScript 调用时,直接执行相应的 Native 代码逻辑,达到 JavaScript 调用 Native 的目的。

说白了就是,Native 往 window 对象挂对象或方法,让 H5 可以调 Native 的方法。具体挂的对象或方法是 Native 定义的,比如人家挂了个getName(arg),H5 调用就是window.getName(arg),当然调用时可以向 Native 传数据。

2.拦截 url scheme原理:先解释一下 url scheme: url scheme 是一种类似于 url 的链接,是为了方便app直接互相调用设计的,形式和普通的 url 近似,主要区别是 protocol 和 host 一般是自定义的,例如: httpsss://bridge_loaded/url?url=http://ymfe.tech,protocol 是 httpsss,host 则是 bridge_loaded。

拦截 url scheme 的主要流程是:**Web 端通过某种方式(例如 iframe.src)发送 url scheme 请求,之后 Native 拦截到请求并根据 url scheme(包括所带的参数)进行相关操作。 **

Native调用H5方法

平台 方法 备注
Android loadurl()
Android evaluateJavascript() Android 4.4 +
IOS(UIwebview) stringByEvaluatingJavaScriptFromString
IOS(UIwebview) JavaScriptCore IOS7+ 支持
IOS(Wkwebview) evaluateJavaScript:javaScriptString IOS8+支持

相比于 JavaScript 调用 Native, Native 调用 JavaScript 较为简单,毕竟不管是 iOS 的 UIWebView 还是 WKWebView,还是 Android 的 WebView 组件,都以子组件的形式存在于 View/Activity 中,直接调用相应的 API 即可。

Native 调用 JavaScript,其实就是执行拼接 JavaScript 字符串,从外部调用 JavaScript 中的方法,因此 JavaScript 的方法必须在全局的 window 上。(闭包里的方法,JavaScript 自己都调用不了,更不用想让 Native 去调用了)

通信原理总结

通信原理是 JSBridge 实现的核心,实现方式可以各种各样,但是万变不离其宗。这里,推荐的实现方式如下:

  • JavaScript 调用 Native 推荐使用 注入 API 的方式(iOS6 忽略,Android 4.2以下使用 WebViewClient 的 onJsPrompt 方式)。
  • Native 调用 JavaScript 则直接执行拼接好的 JavaScript 代码即可。

说实话,作为一个前端开发,刚开始看了上面这些方法啥的,我是一脸懵*。毕竟是嵌套在人家 Native 里面,规则都是他们实现的,我们H5只能遵循这个规则去玩。但一定要理清他们约定的这套规则是如何通信的,才能保证我们的愉快的交流。

通信的原理大概介绍完了,下面介绍实战中如何使用,帮我们更好的理解概念。补充一句,通信的实现方式有很多种,下面只是我司的实现方式:(没有 Native 代码,纯web前端角度)

回到顶部

H5 和 Native 通信实战

因为很多地方需要用同样的方法,比如:关闭H5页面并吐司、上传图片、预览图片、右上角的“增加”(╋)按钮、...所以,我们把和原生通信的方法写在一个js 文件里,直接 export 导出,方便代码的复用。

复制// mob.js
// 判断是什么平台(设备)
var browser = {versions:function(){var u = navigator.userAgent, app = navigator.appVersion;return {trident: u.indexOf('Trident') > -1, // IEpresto: u.indexOf('Presto') > -1, // operawebKit: u.indexOf('AppleWebKit') > -1, // webkitgecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, // firefoxmobile: !!u.match(/AppleWebKit.*Mobile.*/), // mobileios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // iOSandroid: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, // android or uciPhone: u.indexOf('iPhone') > -1 , // iPhone QQHDiPad: u.indexOf('iPad') > -1, // iPadwebApp: u.indexOf('Safari') == -1,teacherApp: u.indexOf('XRJ-Admin') > -1 // 教师端 原生在userAgent放不同的字符串代表不同appguardianApp: u.indexOf('XRJ-Edu') > -1 // 家长端};}(),language: (navigator.browserLanguage || navigator.language).toLowerCase()
}// 判断是否是移动设备打开。browser代码在下面
if(browser.versions.mobile) {var ua = navigator.userAgent.toLowerCase();//获取判断用的对象if (ua.match(/MicroMessenger/i) == "micromessenger") {//在微信中打开}if (ua.match(/WeiBo/i) == "weibo") {//在新浪微博客户端打开}if (ua.match(/QQ/i) == "qq") {//在QQ空间打开}if (browser.versions.ios) {//是否在IOS浏览器打开}if(browser.versions.android){//是否在安卓浏览器打开}
} else {//是PC浏览器打开
}/**
* 我司通信实现方案:
* iOS端注入 WebViewJavascriptBridge 对象或者拦截 url scheme,下面的setupWebViewJavascriptBridge是固定写法
* 这是我司iOS的做法,也是iOS的通用做法。
*
* Android当然也可以这么做,如果他们这样实现,后面H5封装的函数只用写一套就可以适配2端了。
* 我司Android不是这么做的,是通过注入对象或方法实现的,所以下面要针对2端写不同代码。
*/
function setupWebViewJavascriptBridge(callback) {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 = 'https://__bridge_loaded__';document.documentElement.appendChild(WVJBIframe);setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}// 关闭页面并吐丝
// 调用:pageClose(1, '操作成功)
function pageClose(code, msg = ''){setupWebViewJavascriptBridge(function(bridge) {bridge.callHandler('iOS_RESPONSE_CALL_BACK', {"code":code,"msg":msg}, function responseCallback(responseData) {// 关闭页面完毕的回调函数,类似 ajax() 的 success: function(result) {}// responseData:回调函数返回的数据,类似 success 里面的 result});});if(browser.versions.android){if(code == 1){todo.closeWindow(0);}else if(code == 401){todo.refreshWebView;return false;};if(msg) todo.showToast(msg);}
}// 图片上传
// 调用:load()
function load(){setupWebViewJavascriptBridge(function(bridge) {bridge.callHandler('iOS_UPLOAD_PHOTO', [], function responseCallback(responseData) {// 上传完毕回调函数imgload(responseData,true);  // H5 页面中定义的全局方法 window.imgload = function(data,flag) {}})});if(browser.versions.android){picture.showPictureDialog(); // webView.loadUrl("javascript:imgload");}
}// 图片浏览 - data数据格式和原生商量好
// 调用:var imgData = {"position":position,"list":[imgsrc0,imgsrc1,imgsrc2]}; imgSee(imgData);
function imgSee(data){setupWebViewJavascriptBridge(function(bridge) {bridge.callHandler('iOS_PHOTO_BROWSER',data); // 调用 iOS 的 'iOS_PHOTO_BROWSER' 方法,同时传数据data});if(browser.versions.android){data = JSON.stringify(data);todo.startGallery(data); // 调用 Android 的 startGallery 方法,同时传数据data}
}// 单个头部菜单,右上角的“新增”按钮。因为是原生组件,我们操作不到,所以需要初始化页面时往这个按钮上绑定js方法,以便我们之后操作。
// 调用: topMenu('╋',0) || topMenu('新增',0)
function topMenu(title, index){setupWebViewJavascriptBridge(function(bridge) {// js注册方法 'JS_MENU_ACTION' 给 iOS 调用 - 方法名 H5 决定bridge.registerHandler('JS_MENU_ACTION', function(data, responseCallback) {topMenuHandle(data,true); // H5 页面中定义的全局方法 window.topMenuHandle = function(data,flag) {}responseCallback(data); // 做完后,告诉 iOS 一声})// 调 iOS 的 'iOS_MENU_JSON' 并传参bridge.callHandler('iOS_MENU_JSON', [{"action":index,"title":title}]);  // - 方法名 iOS 决定});if(browser.versions.android){// topMenuHandle是往“新增”按钮绑定的js方法,// index是触发这个函数时回传给js的数据,用来判定点击个哪个按钮,title是这个按钮的名字// H5 把一切安排的明明白白的var jsonStr = '[{"action":"javascript:topMenuHandle('+index+')","title":'+title+'}]'menu.inflateMenu(jsonStr);  // jsonStr必须是字符串}
}// 多个头部菜单
// 调用:var titleData = [{'title':"添加行为",'src':base.config.imgHost+"/cs/img/icon_bullet_behavior_add.png"},{'title':"审核行为",'src':base.config.imgHost+"/cs/img/icon_bullet_behavior_review.png"}]
// topMenus(titleData)
function topMenus(data){var dataList = [];$.each(data,function(index,item){dataList.push('{"title":"'+item.title+'","src":"'+item.src+'"'+(browser.versions.android?',"action":"javascript:topMenusHandle('+index+')"':'')+'}');})setupWebViewJavascriptBridge(function(bridge) {bridge.registerHandler('JS_MORE_MENU_ACTION', function(responseData) {topMenusHandle(responseData,true);})bridge.callHandler('iOS_MORE_MENU', dataList)});if(browser.versions.android){menu.inflateCustomMenu('['+dataList+']');}
}// 导出
export { browser,load,imgSee,topMenu,topMenus,pageClose,
}

回到顶部

总结

Hybrid是一种连接 H5 跟 Native 的思路,即可以快速迭代H5功能,又可以有NA的体验,是混合开发的典型开发模式。实践过程中需要根据业务形态模型来定制代码实现,注入时机也不是一成不变的可以根据业务形态来选择。

参考链接:
JSBridge实战
移动混合开发中的 JSBridge
WebViewJavascriptBridge详细使用
Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

JSBridge通信原理相关推荐

  1. JSBridge 技术原理分析

    -     JSBridge的起源    - PhoneGap(Codova 的前身)作为 Hybrid 鼻祖框架,是一个开源的移动开发框架,允许你用标准的web技术-HTML5,CSS3和JavaS ...

  2. webview之JSB通信原理

    1.前言 在这个移动互联网盛行的时代,移动应用的开发就需求量剧增,早期的移动端应用大都使用原生开发(android,ios),而现在的移动开发技术选型上基本都是混合开发(Hybrid),混合开发是一种 ...

  3. ReactNative与iOS通信原理解析-通信篇

    文章首发个人博客: ReactNative与iOS通信原理解析-通信篇 导语:其实原本是想编写一篇  react-native (下文简称 rn) 在  iOS 中如何实现  jsbridge 的文章 ...

  4. JSBridge的原理及使用

    一.什么是JSBridge 主要是给 JavaScript 提供调用 Native 功能的接口,让混合开发中的前端部分可以方便地使用 Native 的功能(例如:地址位置.摄像头). 而且 JSBri ...

  5. jsbridge实现及原理_Android JSBridge的原理与实现

    原标题:Android JSBridge的原理与实现 JSBridge 在Android中,JSBridge已经不是什么新鲜的事物了,各家的实现方式也略有差异.大多数人都知道WebView存在一个漏洞 ...

  6. JSBridge的原理与实现

    为什么要用 JSBridge 顾名思义,JSBridge是js和Native之间通信的桥梁. Android4.2以下,addJavascriptInterface方式有安全漏洞. url schem ...

  7. Binder跨进程通信原理(三):Binder IPC实现原理

    1. 动态内核可加载模块 && 内存映射 正如上一章所说, 跨进程通信是需要内核空间做支持的. 传统的 IPC 机制如 管道, Socket, 都是内核的一部分, 因此通过内核支持来实 ...

  8. Binder跨进程通信原理(一):动态内核加载模块

    先上一张Binder 的工作流程图.(如果不清晰,可以 复制图片链接到浏览器 或 保存到本地 查看,我经常都是这样看图的哈) 一开始上手,陌生的东西比较多,But,其实并不复杂.喔,流程图是用 Pro ...

  9. 传统的Linux中IPC通信原理

    在了解 Binder 跨进程通信原理之前, 我们先了解一下 Linux 传统的进程间通信的概念和基本原理, 这样有助于我们更好的理解 Binder 的通信原理. 这个部分基本都是理论, 基础不是很好的 ...

最新文章

  1. shell getopts命令
  2. OpenGL实现flocking (聚集)实例
  3. 【转】Android中定时器的3种实现方法
  4. 使用方法实现数组的对调与输出
  5. C#中泛型类型约束条件
  6. Xshell连接远程Linux服务器失败Could not connect to ‘192.xxx.xx.xxx‘ (port 22): Connection failed.
  7. cisco初级随堂笔记2
  8. 在Linux上安装Elasticsearch Kibaba.md
  9. Nginx源码目录介绍
  10. 苹果手机来电归属地_手机号码归属地能否取消?工信部回应...
  11. 计算机网络的社会环境分析_2020年昆仑银行分行社会招聘工作人员公告
  12. 铁道部网站登录难点分析
  13. 浅谈 CMap 与 map
  14. Unity接入穿山甲广告(使用unity插件SDK接入)看这一篇就够了
  15. perror和strerror的使用和区别
  16. 中国下一个十年的大趋势
  17. R之方差分析与秩和Kruskal-Wallis
  18. Qt使用qtwebapp编写http服务的步骤
  19. 10组团队项目-Alpha冲刺-2/6
  20. Bartender 4 for Mac(菜单栏应用图标管理)

热门文章

  1. v-if和v-for为什么不能一起使用
  2. 陕西有计算机专业的二本大学排名及分数线,陕西二本录取分数线2021?附陕西二本公办学校实力排名...
  3. Zipkin/Brave 整合Spring-MVC 框架实战
  4. 计算机职称photoshop,计算机职称考试Photoshop6.0图像处理考试大纲
  5. 我对git 、github的初印象
  6. ACSL 美国计算机科学联赛 2016-2017 R4 摩天大楼-Skyscraper 题解
  7. 写字楼价格大跌,摩天大楼面临危机
  8. OpenAI居然能自动写论文?导师直言我都犯难了...
  9. 微软等世界500强公司的面试问题(1)
  10. 步进电机转速与脉冲频率的关系(这篇文章有些地方似乎不对)