webview之JSB通信原理

3,263 阅读8分钟

1、前言

在这个移动互联网盛行的时代,移动应用的开发就需求量剧增,早期的移动端应用大都使用原生开发(android,ios),而现在的移动开发技术选型上基本都是混合开发(Hybrid),混合开发是一种开发模式,指使用多种开发模型开发App,通常会涉及到两大类技术:原生Native、Web H5。

2、WebView

A View that displays web pages.

webView是移动端(原生)提供的运行web的环境,它是一种嵌入式浏览器,原生应用可以用它来展示网络内容。可与页面JavaScript交互,实现混合开发。

1、web的主要元素

在这里插入图片描述

1、html

超文本标记语言, 是一种用来结构化 Web 网页及其内容的标记语言。网页内容可以是:一组段落、一个重点信息列表、也可以含有图片和数据表。

<!DOCTYPE html>
<html>
  <head>
    <title>This is a title</title>
  </head>
  <body>
    <p>Hello world!</p>
  </body>
</html>

html通过HTMLDocmentParser解析之后然后通过HTMLTreeBuilder生成一个Document树 在这里插入图片描述

2、css

CSS 是一种样式规则语言,可将样式应用于 HTML 内容, 例如设置背景颜色和字体,在多个列中布局内容。也可用于创建布局 , 例如将一个单列文本变成包含主要内容区域和存放相关信息的侧边栏区域的布局。

h1 {
    color: red;
    font-size: 5em;
}

css通过CSSParser进行解析如下图 在这里插入图片描述

3、JS

javaScript是一种脚本语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画。


const para = document.querySelector('p');

para.addEventListener('click', updateName);

function updateName() {
  let name = prompt('输入一个新的名字:');
  para.textContent = '玩家1:' + name;
}

JavaScript运行在javaScript engine中,比如Chrome的v8 engine的执行流程如下 在这里插入图片描述

详细流程图。javaScript代码会先被解析生成一棵抽象语法树,然后通过解释器解释生成bytecode 在这里插入图片描述

2、web的渲染流程

在这里插入图片描述

2、webview工作原理

web通过loader加载,然后HTML parser进行解析

image.png

webview通过webkit来渲染web页面 在这里插入图片描述

在Android中渲染流程如下,在ui线程进行绘制的时候,如果有web内容,会调用到webkit thread线程来遍历web 的doc文件,然后生成一个display list,交给ui线程中的display list中进行渲染。 在这里插入图片描述

3、Webview vs native

WebViewNative
加载速度
交互完备
迭代更新
开发成本
原生支持

2、JSBridge基本概念

1、实现 Native端和 web 端双向通信的一种机制。

在Hybrid开发模式下,H5页面经常需要使用到Native的功能,比如打开二维码扫描、调用本地相册、获取用户信息等,同时Native也需要向Web端发送推送、更新状态等。

image.png

2、以 javascript 引擎或 webView 容器为媒介。

webview容器在原生中是一个容器控件,可以加载url或者 web页面,而JavaScript是运行在单独的JS Context中(Webview容器、JSCore等),与原生有运行环境的隔离,所以需要有一种机制实现Native端和Web端的双向通信。

3、通过约定的协议进行通信。

3、JSBridge 的通信原理

Web端和Native可以类比于Client/Server模式,Web端调用原生接口时就如同Client向Server端发送一个请求类似,JSB在此充当类似于HTTP协议的角色。 在这里插入图片描述

实现JSBridge主要是两点:

  1. 将Native端原生接口封装成JavaScript接口
  1. 将Web端JavaScript接口封装成原生接口

1、Native to Web

Native 调用 JS 比较简单,只要 H5 将 JS 方法暴露在 Window 上给 Native 调用即可。

JavaScript作为解释性语言,最大的一个特性就是可以随时随地地通过解释器执行一段JS代码,所以可以将拼接的JavaScript代码字符串,传入JS解析器执行就可以,JS解析器在这里就是webView。

  • Android 4.4之前只能用loadUrl来实现,并且无法执行回调
val jsCode = String.format("window.showWebDialog('%s')", text)
webView.loadUrl("javascript: " + jsCode)
  • Android 4.4之后提供了evaluateJavascript来执行JS代码,并且可以获取返回值执行回调;
String jsCode = String.format("window.showWebDialog('%s')", text);
webView.evaluateJavascript(jsCode, new ValueCallback<String>() {
  @Override
  public void onReceiveValue(String value) {
     //拿到返回值后进行处理   
  }
});
Android 版本API特点
低于4.4WebView.loadUrl无法执行回调
高于4.4WebView.evaluateJavascript可以拿到 JS 执行完毕的返回值
  • iOS的UIWebView使用stringByEvaluatingJavaScriptFromString;
NSString *jsStr = @"执行的JS代码";
[webView stringByEvaluatingJavaScriptFromString:jsStr];
  • iOS的WKWebView使用evaluateJavaScript;
[webView evaluateJavaScript:@"执行的JS代码" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
  
}];
iOS 版本API特点
低于8.0UIWebView.stringByEvaluatingJavaScriptFromString无法执行回调
高于8.0WKWebView.evaluateJavaScript可以拿到 JS 执行完毕的返回值

2、Web to Native

方案调用方法速度注意事项
注入apiaddJavascriptInterface较快Android < 4.2 存在安全漏洞
url拦截shouldOverrideUrlLoading最慢
JS回调时机onJsPrompt较快
日志输出console.log最快scheme://dddd

1、注入 API

将Native的相关接口注入到JS的Context(window)的对象中,一般来说这个对象内的方法名与Native相关方法名是相同的,Web端就可以直接在全局window下使用这个暴露的全局JS对象,进而调用原生端的方法。

image.png

1、 客户端定义js映射对象
 public class AndroidToJS {

    // 定义JS需要调用的方法
    // 被JS调用的方法必须加入@JavascriptInterface注解
    @JavascriptInterface
    public void callAndroid(String msg){
        Log.e("zw","JS调用了Android的callAndroid(),msg : " + msg);
    }}
2、注入js方法
webView.addJavascriptInterface(new AndroidToJS(),"android");
3、前端调用
window.NativeBridge.callAndroid('hello');
4、优缺点

优点:通信时间短,调用方便。

缺点:使用 webView.addJavascriptInterface 方法进行注入。此方法存在漏洞,在 Android4.2 以上提供 @javascriptInterface 注解来规避该漏洞,但对于4.2以下版本则没有任何方法。所以使用该方法有一定的风险和兼容性问题。

2、scheme拦截

客户端和前端定义scheme规范,前端加载scheme,客户端会拦截scheme,如果scheme格式符合规范,客户端会解析scheme中的参数,获取对应的方法和参数名,然后调起客户端原生方法。

image.png

1、前端加载scheme

通过iframe.src发起一个请求,客户端webview能拦截这个请求,做相应的处理。

a标签和location.href都可以做到,但是a灵活性欠缺,需要交互动作触发;location.href连续调用时,后一个请求会覆盖前一个。

var iframe = document.createElement('iframe');  
iframe.style.width = '1px';  
iframe.style.height = '1px';  
iframe.style.display = 'none';  
iframe.src = 'jsbridge://getNetwork?callback=networkInfo';  
document.body.appendChild(iframe);
// 100毫秒后移除
setTimeout(function() {  
    iframe.remove();
}, 100);
2、android 重写shouldOverrideUrlLoading

在webview中重写shouldOverrideUrlLoading根据定义的scheme原则进行拦截

public boolean shouldOverrideUrlLoading(WebView view,
                                        String url) {
  if(isValidScheme()){
      //拦截处理scheme
      return handleScheme()
  } 
  return false;
}
3、优缺点

优点:兼容性好,安卓和 IOS 的各个版本都能支持此功能。

缺点:调用时延比较高 200 - 400ms,在安卓上表现明显;URL scheme 长度有限,内容过多可能会丢失字符;不支持同步返回结果,所有信息传送都需要调用 iframe 请求,使用 callback 得到返回的数据。

3、JS回调时机

image.png

1、Confirm

客户端可以拦截confirm。iOS的UIWebview不支持。使用场景相对较多,不适合用来做jsb。

1、前端调用

window.confirm('dance://app.toast?title=hello')
2、alert

客户端可以拦截alert。iOS的UIWebview不支持。但是alert使用场景较多,不适合用来做jsb。

1、前端调用

window.alert('dance://app.toast?title=hello')
3、Prompt

客户端可以拦截prompt,参数同上。iOS的UIWebview不支持。可自定义返回值,多用于安卓jsb方案。

1、前端调佣

window.prompt('dance://app.toast?title=hello')

2、Android端拦截

/ Java: 重载 onJsPrompt 方法,提取 prompt 内容判断是否需要拦截
    class MyWebViewClient extends WebChromeClient {
      @Override
      public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        if (message.startsWith("bridge://")) {
          // 解析 // 后面的 action 和参数,调用相关的函数
          result.confirm("Yes!");
        }
        return true;
      }
    }
    webView.setWebViewClient(new MyWebViewClient());

4、日志输出

客户端可以拦截console.log,参数同上。安卓和iOS通用。

1、前端调用

console.log('dance://app.toast?title=hello')

2、Android端拦截

WebView.setWebChromeClient(new WebChromeClient() {
  public void onConsoleMessage(String message, int lineNumber, String sourceID) {
    
    return true;
  }
});

4、jsb权限管理

1、jsb权限管理目的

由于第三方应用的存在,为保证端能力调用安全性,防止恶意网页随意获取用户隐私信息,甚至造成用户财产损失等,需要对三方调用jsb的能力进行管理。 暂时无法在飞书文档外展示此内容

2、jsb权限分类

jsb权限分成三种类型:

1、priavte

只有内置的域名才可以访问。

2、protected

默认的jsb都是这种类型,除一方应用之外的域名调用时都会进行权限校验,只有有该权限的jsb才能调用成功。

3、 public

公开的jsb,所有域名的url都可以访问,不涉及到隐私信息和安全问题,不进行权限校验,如op.config。

3、jsb权限管理流程

下面是权限设计的主要流程,每一个非public 类型的jsb调用的时候都会先进行权限校验,只要权限校验成功后才真正调起bridge,执行bridge后给前端回调,如果校验失败则直接返回错误code同时回调给前端。 在这里插入图片描述

参考文档

1、www.alibabacloud.com/blog/in-dep…

2、developer.android.com/reference/a…

3、docs.google.com/presentatio…

4、juejin.cn/post/684700…

5、docs.google.com/presentatio…

6、www.w3schools.com/html/

7、www.educba.com/introductio…

8、chromium.googlesource.com/chromium/sr…